mirror of
https://github.com/github/codeql.git
synced 2026-06-02 20:30:15 +02:00
Compare commits
9 Commits
replace-as
...
erik-krogh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26f81f1c37 | ||
|
|
942392a9a1 | ||
|
|
39820ee561 | ||
|
|
fd581f74aa | ||
|
|
21bec8a3d3 | ||
|
|
108a0bbd29 | ||
|
|
f735821630 | ||
|
|
98acb1db01 | ||
|
|
ade7130935 |
2
.bazelrc
2
.bazelrc
@@ -1,3 +1,3 @@
|
||||
build --repo_env=CC=clang --repo_env=CXX=clang++ --cxxopt="-std=c++17"
|
||||
build --repo_env=CC=clang --repo_env=CXX=clang++ --copt="-std=c++17"
|
||||
|
||||
try-import %workspace%/local.bazelrc
|
||||
|
||||
1
.github/labeler.yml
vendored
1
.github/labeler.yml
vendored
@@ -42,4 +42,3 @@ documentation:
|
||||
|
||||
"QL-for-QL":
|
||||
- ql/**/*
|
||||
- .github/workflows/ql-for-ql*
|
||||
|
||||
3
.github/workflows/check-qldoc.yml
vendored
3
.github/workflows/check-qldoc.yml
vendored
@@ -27,8 +27,7 @@ jobs:
|
||||
run: |
|
||||
EXIT_CODE=0
|
||||
# TODO: remove the swift exception from the regex when we fix generated QLdoc
|
||||
# TODO: remove the shared exception from the regex when coverage of qlpacks without dbschemes is supported
|
||||
changed_lib_packs="$(git diff --name-only --diff-filter=ACMRT HEAD^ HEAD | { grep -Po '^(?!(swift|shared))[a-z]*/ql/lib' || true; } | sort -u)"
|
||||
changed_lib_packs="$(git diff --name-only --diff-filter=ACMRT HEAD^ HEAD | { grep -Po '^(?!swift)[a-z]*/ql/lib' || true; } | sort -u)"
|
||||
for pack_dir in ${changed_lib_packs}; do
|
||||
lang="${pack_dir%/ql/lib}"
|
||||
codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-current.txt" --dir="${pack_dir}"
|
||||
|
||||
2
.github/workflows/close-stale.yml
vendored
2
.github/workflows/close-stale.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v6
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `Stale` label in order to avoid having this issue closed in 7 days.'
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
# uses a compiled language
|
||||
|
||||
- run: |
|
||||
dotnet build csharp
|
||||
dotnet build csharp /p:UseSharedCompilation=false
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@main
|
||||
|
||||
2
.github/workflows/csv-coverage-metrics.yml
vendored
2
.github/workflows/csv-coverage-metrics.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
||||
DATABASE="${{ runner.temp }}/csharp-database"
|
||||
PROJECT="${{ runner.temp }}/csharp-project"
|
||||
dotnet new classlib --language=C# --output="$PROJECT"
|
||||
codeql database create "$DATABASE" --language=csharp --source-root="$PROJECT" --command 'dotnet build /t:rebuild csharp-project.csproj'
|
||||
codeql database create "$DATABASE" --language=csharp --source-root="$PROJECT" --command 'dotnet build /t:rebuild csharp-project.csproj /p:UseSharedCompilation=false'
|
||||
- name: Capture coverage information
|
||||
run: |
|
||||
DATABASE="${{ runner.temp }}/csharp-database"
|
||||
|
||||
14
.github/workflows/go-tests.yml
vendored
14
.github/workflows/go-tests.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
||||
name: Test Linux (Ubuntu)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.18.1
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.18.1
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
|
||||
|
||||
- name: Upload qhelp markdown
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: qhelp-markdown
|
||||
path: go/qhelp-out/**/*.md
|
||||
@@ -57,10 +57,10 @@ jobs:
|
||||
name: Test MacOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.18.1
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.18.1
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
@@ -87,10 +87,10 @@ jobs:
|
||||
name: Test Windows
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.18.1
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.18.1
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
|
||||
4
.github/workflows/qhelp-pr-preview.yml
vendored
4
.github/workflows/qhelp-pr-preview.yml
vendored
@@ -27,7 +27,7 @@ on:
|
||||
- main
|
||||
- "rc/*"
|
||||
paths:
|
||||
- "**/*.qhelp"
|
||||
- "ruby/**/*.qhelp"
|
||||
|
||||
jobs:
|
||||
qhelp:
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
id: changes
|
||||
run: |
|
||||
(git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.qhelp$' | grep -z -v '.inc.qhelp';
|
||||
git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.inc.qhelp$' | xargs --null -rn1 basename -z | xargs --null -rn1 git grep -z -l) |
|
||||
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) |
|
||||
grep -z '.qhelp$' | grep -z -v '^-' | sort -z -u > "${RUNNER_TEMP}/paths.txt"
|
||||
|
||||
- name: QHelp preview
|
||||
|
||||
138
.github/workflows/ql-for-ql-build.yml
vendored
138
.github/workflows/ql-for-ql-build.yml
vendored
@@ -5,13 +5,6 @@ on:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "ql/**"
|
||||
- "**.qll"
|
||||
- "**.ql"
|
||||
- "**.dbscheme"
|
||||
- "**/qlpack.yml"
|
||||
- ".github/workflows/ql-for-ql-build.yml"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -24,7 +17,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- name: Get CodeQL version
|
||||
@@ -34,37 +27,31 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Cache entire pack
|
||||
id: cache-pack
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ runner.temp }}/pack
|
||||
key: ${{ runner.os }}-pack-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/**/qlpack.yml') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}--${{ hashFiles('.github/workflows/ql-for-ql-build.yml') }}
|
||||
- name: Cache queries
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
id: cache-queries
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ runner.temp }}/queries
|
||||
key: queries-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/**/qlpack.yml') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}--${{ hashFiles('.github/workflows/ql-for-ql-build.yml') }}
|
||||
path: ${{ runner.temp }}/query-pack.zip
|
||||
key: queries-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/**/qlpack.yml') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}
|
||||
- name: Build query pack
|
||||
if: steps.cache-queries.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
if: steps.cache-queries.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd ql/ql/src
|
||||
"${CODEQL}" pack create -j 16
|
||||
mv .codeql/pack/codeql/ql/0.0.0 ${{ runner.temp }}/queries
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Move cache queries to pack
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cp -r ${{ runner.temp }}/queries ${{ runner.temp }}/pack
|
||||
"${CODEQL}" pack create
|
||||
cd .codeql/pack/codeql/ql/0.0.0
|
||||
zip "${PACKZIP}" -r .
|
||||
rm -rf *
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
PACKZIP: ${{ runner.temp }}/query-pack.zip
|
||||
- name: Upload query pack
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: query-pack-zip
|
||||
path: ${{ runner.temp }}/query-pack.zip
|
||||
|
||||
### Build the extractor ###
|
||||
- name: Cache entire extractor
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
id: cache-extractor
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@@ -75,7 +62,7 @@ jobs:
|
||||
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' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -84,38 +71,76 @@ jobs:
|
||||
ql/target
|
||||
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
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' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
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' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
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' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
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' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
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@v3
|
||||
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 the queries and extractor ###
|
||||
- name: Package pack
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cp -r ql/codeql-extractor.yml ql/tools ql/ql/src/ql.dbscheme.stats ${PACK}/
|
||||
mkdir -p ${PACK}/tools/linux64
|
||||
cp ql/target/release/ql-autobuilder ${PACK}/tools/linux64/autobuilder
|
||||
cp ql/target/release/ql-extractor ${PACK}/tools/linux64/extractor
|
||||
chmod +x ${PACK}/tools/linux64/autobuilder
|
||||
chmod +x ${PACK}/tools/linux64/extractor
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: query-pack-zip
|
||||
path: query-pack-zip
|
||||
- uses: actions/download-artifact@v3
|
||||
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 .
|
||||
rm -rf *
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: codeql-ql-pack
|
||||
path: codeql-ql.zip
|
||||
retention-days: 1
|
||||
|
||||
### Run the analysis ###
|
||||
- name: Download pack
|
||||
uses: actions/download-artifact@v3
|
||||
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}" '.database."run-queries"=["--search-path", $pack] | .resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .resolve.languages=["--search-path", $pack] | .database.init=["--search-path", $pack]')
|
||||
JSON=$(jq -nc --arg pack "${PACK}" '.database."run-queries"=["--search-path", $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
|
||||
@@ -123,36 +148,31 @@ jobs:
|
||||
- name: Create CodeQL config file
|
||||
run: |
|
||||
echo "paths-ignore:" >> ${CONF}
|
||||
echo " - ql/ql/test" >> ${CONF}
|
||||
echo " - \"*/ql/lib/upgrades/\"" >> ${CONF}
|
||||
echo " - ql/ql/test" >> ${CONF}
|
||||
echo " - \"*/ql/lib/upgrades/\"" >> ${CONF}
|
||||
echo "disable-default-queries: true" >> ${CONF}
|
||||
echo "queries:" >> ${CONF}
|
||||
echo " - uses: ./ql/ql/src/codeql-suites/ql-code-scanning.qls" >> ${CONF}
|
||||
echo "packs:" >> ${CONF}
|
||||
echo " - codeql/ql" >> ${CONF}
|
||||
echo "Config file: "
|
||||
cat ${CONF}
|
||||
env:
|
||||
env:
|
||||
CONF: ./ql-for-ql-config.yml
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
|
||||
with:
|
||||
languages: ql
|
||||
db-location: ${{ runner.temp }}/db
|
||||
config-file: ./ql-for-ql-config.yml
|
||||
- name: Move pack cache
|
||||
run: |
|
||||
cp -r ${PACK}/.cache ql/ql/src/.cache
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
with:
|
||||
uses: github/codeql-action/analyze@aa93aea877e5fb8841bcb1193f672abf6e9f2980
|
||||
with:
|
||||
category: "ql-for-ql"
|
||||
- name: Copy sarif file to CWD
|
||||
run: cp ../results/ql.sarif ./ql-for-ql.sarif
|
||||
- name: Fixup the $scema in sarif # Until https://github.com/microsoft/sarif-vscode-extension/pull/436/ is part in a stable release
|
||||
run: |
|
||||
sed -i 's/\$schema.*/\$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0",/' ql-for-ql.sarif
|
||||
sed -i 's/\$schema.*/\$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0",/' ql-for-ql.sarif
|
||||
- name: Sarif as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: actions/cache@v3
|
||||
|
||||
2
.github/workflows/ql-for-ql-tests.yml
vendored
2
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: actions/cache@v3
|
||||
|
||||
3
.github/workflows/ruby-build.yml
vendored
3
.github/workflows/ruby-build.yml
vendored
@@ -95,7 +95,6 @@ jobs:
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
- name: Build Query Pack
|
||||
run: |
|
||||
codeql pack create ../shared/ssa --output target/packs
|
||||
codeql pack create ql/lib --output target/packs
|
||||
codeql pack install ql/src
|
||||
codeql pack create ql/src --output target/packs
|
||||
@@ -197,7 +196,7 @@ jobs:
|
||||
- name: Prepare test files
|
||||
shell: bash
|
||||
run: |
|
||||
echo "import codeql.ruby.AST select count(File f)" > "test.ql"
|
||||
echo "import ruby select count(File f)" > "test.ql"
|
||||
echo "| 4 |" > "test.expected"
|
||||
echo 'name: sample-tests
|
||||
version: 0.0.0
|
||||
|
||||
2
.github/workflows/swift-codegen.yml
vendored
2
.github/workflows/swift-codegen.yml
vendored
@@ -4,8 +4,6 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift-codegen.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
branches:
|
||||
|
||||
10
.github/workflows/swift-integration-tests.yml
vendored
10
.github/workflows/swift-integration-tests.yml
vendored
@@ -4,8 +4,6 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift-integration-tests.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
@@ -32,14 +30,6 @@ jobs:
|
||||
- name: Build Swift extractor
|
||||
run: |
|
||||
bazel run //swift:create-extractor-pack
|
||||
- name: Get Swift version
|
||||
id: get_swift_version
|
||||
run: |
|
||||
VERSION=$(bazel run //swift/extractor -- --version | sed -ne 's/.*version \(\S*\).*/\1/p')
|
||||
echo "::set-output name=version::$VERSION"
|
||||
- uses: swift-actions/setup-swift@v1
|
||||
with:
|
||||
swift-version: "${{steps.get_swift_version.outputs.version}}"
|
||||
- name: Run integration tests
|
||||
run: |
|
||||
python integration-tests/runner.py
|
||||
|
||||
2
.github/workflows/swift-qltest.yml
vendored
2
.github/workflows/swift-qltest.yml
vendored
@@ -4,8 +4,6 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift-qltest.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
|
||||
# Bazel (excluding BUILD.bazel files)
|
||||
WORKSPACE.bazel @github/codeql-ci-reviewers
|
||||
.bazelversion @github/codeql-ci-reviewers
|
||||
.bazelrc @github/codeql-ci-reviewers
|
||||
**/*.bzl @github/codeql-ci-reviewers
|
||||
|
||||
# Documentation etc
|
||||
|
||||
@@ -4,7 +4,8 @@ This open source repository contains the standard CodeQL libraries and queries t
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL using the [CodeQL extension for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) and the [CodeQL CLI](https://codeql.github.com/docs/codeql-cli/).
|
||||
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
|
||||
You can use the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension or the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com (Semmle Legacy product) to try out your queries on any open source project that's currently being analyzed.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ provide:
|
||||
- "*/ql/test/qlpack.yml"
|
||||
- "*/ql/examples/qlpack.yml"
|
||||
- "*/ql/consistency-queries/qlpack.yml"
|
||||
- "shared/*/qlpack.yml"
|
||||
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
|
||||
- "go/ql/config/legacy-support/qlpack.yml"
|
||||
- "go/build/codeql-extractor-go/codeql-extractor.yml"
|
||||
|
||||
@@ -8,19 +8,15 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/DataFlowImplLocal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
@@ -33,30 +29,24 @@
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Common": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
|
||||
],
|
||||
"TaintTracking::Configuration Java/C++/C#/Python": [
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
@@ -70,23 +60,22 @@
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttrackingforlibraries/TaintTrackingImpl.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Consistency checks": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
],
|
||||
"DataFlow Java/C#/Ruby/Python/Swift Flow Summaries": [
|
||||
"DataFlow Java/C# Flow Summaries": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
|
||||
],
|
||||
"SsaReadPosition Java/C#": [
|
||||
@@ -115,7 +104,7 @@
|
||||
],
|
||||
"C++ SubBasicBlocks": [
|
||||
"cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/old/internal/SubBasicBlocks.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
|
||||
],
|
||||
"IR Instruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
@@ -471,6 +460,15 @@
|
||||
"javascript/ql/lib/IDEContextual.qll",
|
||||
"python/ql/lib/analysis/IDEContextual.qll"
|
||||
],
|
||||
"SSA C#": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/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/cil/internal/SsaImplCommon.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/SsaImplCommon.qll"
|
||||
],
|
||||
"CryptoAlgorithms Python/JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll",
|
||||
@@ -542,7 +540,7 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
|
||||
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/AccessPathSyntax.qll"
|
||||
],
|
||||
"IncompleteUrlSubstringSanitization": [
|
||||
@@ -586,22 +584,22 @@
|
||||
],
|
||||
"Swift declarations test file": [
|
||||
"swift/ql/test/extractor-tests/declarations/declarations.swift",
|
||||
"swift/ql/test/library-tests/ast/declarations.swift"
|
||||
"swift/ql/test/library-tests/parent/declarations.swift"
|
||||
],
|
||||
"Swift statements test file": [
|
||||
"swift/ql/test/extractor-tests/statements/statements.swift",
|
||||
"swift/ql/test/library-tests/ast/statements.swift"
|
||||
"swift/ql/test/library-tests/parent/statements.swift"
|
||||
],
|
||||
"Swift expressions test file": [
|
||||
"swift/ql/test/extractor-tests/expressions/expressions.swift",
|
||||
"swift/ql/test/library-tests/ast/expressions.swift"
|
||||
"swift/ql/test/library-tests/parent/expressions.swift"
|
||||
],
|
||||
"Swift patterns test file": [
|
||||
"swift/ql/test/extractor-tests/patterns/patterns.swift",
|
||||
"swift/ql/test/library-tests/ast/patterns.swift"
|
||||
"swift/ql/test/library-tests/parent/patterns.swift"
|
||||
],
|
||||
"IncompleteMultiCharacterSanitization JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
{
|
||||
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
|
||||
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /p:UseSharedCompilation=false /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0;
|
||||
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
|
||||
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
|
||||
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
class Expr extends @expr {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Location extends @location_expr {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
predicate isExprWithNewBuiltin(Expr expr) {
|
||||
exists(int kind | exprs(expr, kind, _) | 336 <= kind and kind <= 362)
|
||||
}
|
||||
|
||||
from Expr expr, int kind, int kind_new, Location location
|
||||
where
|
||||
exprs(expr, kind, location) and
|
||||
if isExprWithNewBuiltin(expr) then kind_new = 1 else kind_new = kind
|
||||
select expr, kind_new, location
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
description: Add new builtin operations
|
||||
compatibility: partial
|
||||
exprs.rel: run exprs.qlo
|
||||
@@ -1,40 +1,3 @@
|
||||
## 0.4.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added subclasses of `BuiltInOperations` for `__is_same`, `__is_function`, `__is_layout_compatible`, `__is_pointer_interconvertible_base_of`, `__is_array`, `__array_rank`, `__array_extent`, `__is_arithmetic`, `__is_complete_type`, `__is_compound`, `__is_const`, `__is_floating_point`, `__is_fundamental`, `__is_integral`, `__is_lvalue_reference`, `__is_member_function_pointer`, `__is_member_object_pointer`, `__is_member_pointer`, `__is_object`, `__is_pointer`, `__is_reference`, `__is_rvalue_reference`, `__is_scalar`, `__is_signed`, `__is_unsigned`, `__is_void`, and `__is_volatile`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed an issue in the taint tracking analysis where implicit reads were not allowed by default in sinks or additional taint steps that used flow states.
|
||||
|
||||
## 0.3.5
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added support for getting the link targets of global and namespace variables.
|
||||
* Added a `BlockAssignExpr` class, which models a `memcpy`-like operation used in compiler generated copy/move constructors and assignment operations.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added a `BlockAssignExpr` class, which models a `memcpy`-like operation used in compiler generated copy/move constructors and assignment operations.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been
|
||||
deleted.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added support for getting the link targets of global and namespace variables.
|
||||
5
cpp/ql/lib/change-notes/2022-08-22-xml-rename.md
Normal file
5
cpp/ql/lib/change-notes/2022-08-22-xml-rename.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* Classes/predicates that had upper-case acronym XML in their name have been renamed to Xml to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
@@ -1,15 +0,0 @@
|
||||
## 0.3.4
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added support for getting the link targets of global and namespace variables.
|
||||
* Added a `BlockAssignExpr` class, which models a `memcpy`-like operation used in compiler generated copy/move constructors and assignment operations.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
@@ -1 +0,0 @@
|
||||
## 0.3.5
|
||||
@@ -1,14 +0,0 @@
|
||||
## 0.4.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added subclasses of `BuiltInOperations` for `__is_same`, `__is_function`, `__is_layout_compatible`, `__is_pointer_interconvertible_base_of`, `__is_array`, `__array_rank`, `__array_extent`, `__is_arithmetic`, `__is_complete_type`, `__is_compound`, `__is_const`, `__is_floating_point`, `__is_fundamental`, `__is_integral`, `__is_lvalue_reference`, `__is_member_function_pointer`, `__is_member_object_pointer`, `__is_member_pointer`, `__is_object`, `__is_pointer`, `__is_reference`, `__is_rvalue_reference`, `__is_scalar`, `__is_signed`, `__is_unsigned`, `__is_void`, and `__is_volatile`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed an issue in the taint tracking analysis where implicit reads were not allowed by default in sinks or additional taint steps that used flow states.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.4.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.1
|
||||
lastReleaseVersion: 0.3.3
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
|
||||
module ProductFlow {
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `(source1, source2)` is a relevant data flow source.
|
||||
*
|
||||
* `source1` and `source2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSourcePair(DataFlow::Node source1, DataFlow::Node source2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `(source1, source2)` is a relevant data flow source with initial states `state1`
|
||||
* and `state2`, respectively.
|
||||
*
|
||||
* `source1` and `source2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSourcePair(
|
||||
DataFlow::Node source1, DataFlow::FlowState state1, DataFlow::Node source2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 = "" and
|
||||
state2 = "" and
|
||||
this.isSourcePair(source1, source2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(sink1, sink2)` is a relevant data flow sink.
|
||||
*
|
||||
* `sink1` and `sink2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSinkPair(DataFlow::Node sink1, DataFlow::Node sink2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `(sink1, sink2)` is a relevant data flow sink with final states `state1`
|
||||
* and `state2`, respectively.
|
||||
*
|
||||
* `sink1` and `sink2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSinkPair(
|
||||
DataFlow::Node sink1, DataFlow::FlowState state1, DataFlow::Node sink2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 = "" and
|
||||
state2 = "" and
|
||||
this.isSinkPair(sink1, sink2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited through the first projection of the product
|
||||
* dataflow graph when the flow state is `state`.
|
||||
*/
|
||||
predicate isBarrier1(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isBarrier1(node) and state = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited through the second projection of the product
|
||||
* dataflow graph when the flow state is `state`.
|
||||
*/
|
||||
predicate isBarrier2(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isBarrier2(node) and state = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited through the first projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrier1(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited through the second projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrier2(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow out of `node` is prohibited in the first projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrierOut1(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow out of `node` is prohibited in the second projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrierOut2(DataFlow::Node node) { none() }
|
||||
|
||||
/*
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
|
||||
* the first projection of the product dataflow graph.
|
||||
*/
|
||||
|
||||
predicate isAdditionalFlowStep1(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
|
||||
* the first projection of the product dataflow graph.
|
||||
*
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep1(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 instanceof DataFlow::FlowStateEmpty and
|
||||
state2 instanceof DataFlow::FlowStateEmpty and
|
||||
this.isAdditionalFlowStep1(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
|
||||
* the second projection of the product dataflow graph.
|
||||
*/
|
||||
predicate isAdditionalFlowStep2(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
|
||||
* the second projection of the product dataflow graph.
|
||||
*
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep2(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 instanceof DataFlow::FlowStateEmpty and
|
||||
state2 instanceof DataFlow::FlowStateEmpty and
|
||||
this.isAdditionalFlowStep2(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow into `node` is prohibited in the first projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrierIn1(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow into `node` is prohibited in the second projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrierIn2(DataFlow::Node node) { none() }
|
||||
|
||||
predicate hasFlowPath(
|
||||
DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1,
|
||||
DataFlow2::PathNode sink2
|
||||
) {
|
||||
reachable(this, source1, source2, sink1, sink2)
|
||||
}
|
||||
}
|
||||
|
||||
private import Internal
|
||||
|
||||
module Internal {
|
||||
class Conf1 extends DataFlow::Configuration {
|
||||
Conf1() { this = "Conf1" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
|
||||
exists(Configuration conf | conf.isSourcePair(source, state, _, _))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
|
||||
exists(Configuration conf | conf.isSinkPair(sink, state, _, _))
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
exists(Configuration conf | conf.isBarrier1(node, state))
|
||||
}
|
||||
|
||||
override predicate isBarrierOut(DataFlow::Node node) {
|
||||
exists(Configuration conf | conf.isBarrierOut1(node))
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
exists(Configuration conf | conf.isAdditionalFlowStep1(node1, state1, node2, state2))
|
||||
}
|
||||
|
||||
override predicate isBarrierIn(DataFlow::Node node) {
|
||||
exists(Configuration conf | conf.isBarrierIn1(node))
|
||||
}
|
||||
}
|
||||
|
||||
class Conf2 extends DataFlow2::Configuration {
|
||||
Conf2() { this = "Conf2" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
|
||||
exists(Configuration conf, DataFlow::PathNode source1 |
|
||||
conf.isSourcePair(source1.getNode(), source1.getState(), source, state) and
|
||||
any(Conf1 c).hasFlowPath(source1, _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
|
||||
exists(Configuration conf, DataFlow::PathNode sink1 |
|
||||
conf.isSinkPair(sink1.getNode(), sink1.getState(), sink, state) and
|
||||
any(Conf1 c).hasFlowPath(_, sink1)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
exists(Configuration conf | conf.isBarrier2(node, state))
|
||||
}
|
||||
|
||||
override predicate isBarrierOut(DataFlow::Node node) {
|
||||
exists(Configuration conf | conf.isBarrierOut2(node))
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
exists(Configuration conf | conf.isAdditionalFlowStep2(node1, state1, node2, state2))
|
||||
}
|
||||
|
||||
override predicate isBarrierIn(DataFlow::Node node) {
|
||||
exists(Configuration conf | conf.isBarrierIn2(node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachableInterprocEntry(
|
||||
Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
|
||||
DataFlow::PathNode node1, DataFlow2::PathNode node2
|
||||
) {
|
||||
conf.isSourcePair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState()) and
|
||||
node1 = source1 and
|
||||
node2 = source2
|
||||
or
|
||||
exists(
|
||||
DataFlow::PathNode midEntry1, DataFlow2::PathNode midEntry2, DataFlow::PathNode midExit1,
|
||||
DataFlow2::PathNode midExit2
|
||||
|
|
||||
reachableInterprocEntry(conf, source1, source2, midEntry1, midEntry2) and
|
||||
interprocEdgePair(midExit1, midExit2, node1, node2) and
|
||||
localPathStep1*(midEntry1, midExit1) and
|
||||
localPathStep2*(midEntry2, midExit2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate localPathStep1(DataFlow::PathNode pred, DataFlow::PathNode succ) {
|
||||
DataFlow::PathGraph::edges(pred, succ) and
|
||||
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
|
||||
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
|
||||
}
|
||||
|
||||
private predicate localPathStep2(DataFlow2::PathNode pred, DataFlow2::PathNode succ) {
|
||||
DataFlow2::PathGraph::edges(pred, succ) and
|
||||
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
|
||||
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate interprocEdge1(
|
||||
Declaration predDecl, Declaration succDecl, DataFlow::PathNode pred1, DataFlow::PathNode succ1
|
||||
) {
|
||||
DataFlow::PathGraph::edges(pred1, succ1) and
|
||||
predDecl != succDecl and
|
||||
pred1.getNode().getEnclosingCallable() = predDecl and
|
||||
succ1.getNode().getEnclosingCallable() = succDecl
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate interprocEdge2(
|
||||
Declaration predDecl, Declaration succDecl, DataFlow2::PathNode pred2, DataFlow2::PathNode succ2
|
||||
) {
|
||||
DataFlow2::PathGraph::edges(pred2, succ2) and
|
||||
predDecl != succDecl and
|
||||
pred2.getNode().getEnclosingCallable() = predDecl and
|
||||
succ2.getNode().getEnclosingCallable() = succDecl
|
||||
}
|
||||
|
||||
private predicate interprocEdgePair(
|
||||
DataFlow::PathNode pred1, DataFlow2::PathNode pred2, DataFlow::PathNode succ1,
|
||||
DataFlow2::PathNode succ2
|
||||
) {
|
||||
exists(Declaration predDecl, Declaration succDecl |
|
||||
interprocEdge1(predDecl, succDecl, pred1, succ1) and
|
||||
interprocEdge2(predDecl, succDecl, pred2, succ2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reachable(
|
||||
Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
|
||||
DataFlow::PathNode sink1, DataFlow2::PathNode sink2
|
||||
) {
|
||||
exists(DataFlow::PathNode mid1, DataFlow2::PathNode mid2 |
|
||||
reachableInterprocEntry(conf, source1, source2, mid1, mid2) and
|
||||
conf.isSinkPair(sink1.getNode(), sink1.getState(), sink2.getNode(), sink2.getState()) and
|
||||
localPathStep1*(mid1, sink1) and
|
||||
localPathStep2*(mid2, sink2)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Provides a library for local (intra-procedural) and global (inter-procedural)
|
||||
* data flow analysis: deciding whether data can flow from a _source_ to a
|
||||
* _sink_. This library differs from the one in `semmle.code.cpp.dataflow` in that
|
||||
* this library uses the IR (Intermediate Representation) library, which provides
|
||||
* a more precise semantic representation of the program, whereas the other dataflow
|
||||
* library uses the more syntax-oriented ASTs. This library should provide more accurate
|
||||
* results than the AST-based library in most scenarios.
|
||||
*
|
||||
* Unless configured otherwise, _flow_ means that the exact value of
|
||||
* the source may reach the sink. We do not track flow across pointer
|
||||
* dereferences or array indexing.
|
||||
*
|
||||
* To use global (interprocedural) data flow, extend the class
|
||||
* `DataFlow::Configuration` as documented on that class. To use local
|
||||
* (intraprocedural) data flow between expressions, call
|
||||
* `DataFlow::localExprFlow`. For more general cases of local data flow, call
|
||||
* `DataFlow::localFlow` or `DataFlow::localFlowStep` with arguments of type
|
||||
* `DataFlow::Node`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
module DataFlow {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Provides a `DataFlow2` module, which is a copy of the `DataFlow` module. Use
|
||||
* this class when data-flow configurations must depend on each other. Two
|
||||
* classes extending `DataFlow::Configuration` should never depend on each
|
||||
* other, but one of them should instead depend on a
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
module DataFlow2 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl2
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Provides a `DataFlow3` module, which is a copy of the `DataFlow` module. Use
|
||||
* this class when data-flow configurations must depend on each other. Two
|
||||
* classes extending `DataFlow::Configuration` should never depend on each
|
||||
* other, but one of them should instead depend on a
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
module DataFlow3 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl3
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Provides a `DataFlow4` module, which is a copy of the `DataFlow` module. Use
|
||||
* this class when data-flow configurations must depend on each other. Two
|
||||
* classes extending `DataFlow::Configuration` should never depend on each
|
||||
* other, but one of them should instead depend on a
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
module DataFlow4 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl4
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Provides a predicate for non-contextual virtual dispatch and function
|
||||
* pointer resolution.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import internal.DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Resolve potential target function(s) for `call`.
|
||||
*
|
||||
* If `call` is a call through a function pointer (`ExprCall`) or its target is
|
||||
* a virtual member function, simple data flow analysis is performed in order
|
||||
* to identify the possible target(s).
|
||||
*/
|
||||
Function resolveCall(Call call) {
|
||||
exists(CallInstruction callInstruction |
|
||||
callInstruction.getAst() = call and
|
||||
result = viableCallable(callInstruction)
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*
|
||||
* We define _taint propagation_ informally to mean that a substantial part of
|
||||
* the information from the source is preserved at the sink. For example, taint
|
||||
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
|
||||
* 100` since we consider a single bit of information to be too little.
|
||||
*
|
||||
* To use global (interprocedural) taint tracking, extend the class
|
||||
* `TaintTracking::Configuration` as documented on that class. To use local
|
||||
* (intraprocedural) taint tracking between expressions, call
|
||||
* `TaintTracking::localExprTaint`. For more general cases of local taint
|
||||
* tracking, call `TaintTracking::localTaint` or
|
||||
* `TaintTracking::localTaintStep` with arguments of type `DataFlow::Node`.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
|
||||
module TaintTracking {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Provides a `TaintTracking2` module, which is a copy of the `TaintTracking`
|
||||
* module. Use this class when data-flow configurations or taint-tracking
|
||||
* configurations must depend on each other. Two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other, but one of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. The
|
||||
* `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and
|
||||
* `TaintTracking2::Configuration` extends `DataFlow2::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.TaintTracking` for the full documentation.
|
||||
*/
|
||||
module TaintTracking2 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking2.TaintTrackingImpl
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Provides a `TaintTracking3` module, which is a copy of the `TaintTracking`
|
||||
* module. Use this class when data-flow configurations or taint-tracking
|
||||
* configurations must depend on each other. Two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other, but one of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. The
|
||||
* `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and
|
||||
* `TaintTracking2::Configuration` extends `DataFlow2::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.TaintTracking` for the full documentation.
|
||||
*/
|
||||
module TaintTracking3 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking3.TaintTrackingImpl
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
cached
|
||||
Function viableCallable(CallInstruction call) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
result = call.getStaticCallTarget()
|
||||
or
|
||||
// If the target of the call does not have a body in the snapshot, it might
|
||||
// be because the target is just a header declaration, and the real target
|
||||
// will be determined at run time when the caller and callee are linked
|
||||
// together by the operating system's dynamic linker. In case a _unique_
|
||||
// function with the right signature is present in the database, we return
|
||||
// that as a potential callee.
|
||||
exists(string qualifiedName, int nparams |
|
||||
callSignatureWithoutBody(qualifiedName, nparams, call) and
|
||||
functionSignatureWithBody(qualifiedName, nparams, result) and
|
||||
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
|
||||
)
|
||||
or
|
||||
// Virtual dispatch
|
||||
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides virtual dispatch support compatible with the original
|
||||
* implementation of `semmle.code.cpp.security.TaintTracking`.
|
||||
*/
|
||||
private module VirtualDispatch {
|
||||
/** A call that may dispatch differently depending on the qualifier value. */
|
||||
abstract class DataSensitiveCall extends DataFlowCall {
|
||||
/**
|
||||
* Gets the node whose value determines the target of this call. This node
|
||||
* could be the qualifier of a virtual dispatch or the function-pointer
|
||||
* expression in a call to a function pointer. What they have in common is
|
||||
* that we need to find out which data flows there, and then it's up to the
|
||||
* `resolve` predicate to stitch that information together and resolve the
|
||||
* call.
|
||||
*/
|
||||
abstract DataFlow::Node getDispatchValue();
|
||||
|
||||
/** Gets a candidate target for this call. */
|
||||
abstract Function resolve();
|
||||
|
||||
/**
|
||||
* Whether `src` can flow to this call.
|
||||
*
|
||||
* Searches backwards from `getDispatchValue()` to `src`. The `allowFromArg`
|
||||
* parameter is true when the search is allowed to continue backwards into
|
||||
* a parameter; non-recursive callers should pass `_` for `allowFromArg`.
|
||||
*/
|
||||
predicate flowsFrom(DataFlow::Node src, boolean allowFromArg) {
|
||||
src = this.getDispatchValue() and allowFromArg = true
|
||||
or
|
||||
exists(DataFlow::Node other, boolean allowOtherFromArg |
|
||||
this.flowsFrom(other, allowOtherFromArg)
|
||||
|
|
||||
// Call argument
|
||||
exists(DataFlowCall call, Position i |
|
||||
other
|
||||
.(DataFlow::ParameterNode)
|
||||
.isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
|
||||
src.(ArgumentNode).argumentOf(call, pragma[only_bind_into](pragma[only_bind_out](i)))
|
||||
) and
|
||||
allowOtherFromArg = true and
|
||||
allowFromArg = true
|
||||
or
|
||||
// Call return
|
||||
exists(DataFlowCall call, ReturnKind returnKind |
|
||||
other = getAnOutNode(call, returnKind) and
|
||||
returnNodeWithKindAndEnclosingCallable(src, returnKind, call.getStaticCallTarget())
|
||||
) and
|
||||
allowFromArg = false
|
||||
or
|
||||
// Local flow
|
||||
DataFlow::localFlowStep(src, other) and
|
||||
allowFromArg = allowOtherFromArg
|
||||
or
|
||||
// Flow from global variable to load.
|
||||
exists(LoadInstruction load, GlobalOrNamespaceVariable var |
|
||||
var = src.asVariable() and
|
||||
other.asInstruction() = load and
|
||||
addressOfGlobal(load.getSourceAddress(), var) and
|
||||
// The `allowFromArg` concept doesn't play a role when `src` is a
|
||||
// global variable, so we just set it to a single arbitrary value for
|
||||
// performance.
|
||||
allowFromArg = true
|
||||
)
|
||||
or
|
||||
// Flow from store to global variable.
|
||||
exists(StoreInstruction store, GlobalOrNamespaceVariable var |
|
||||
var = other.asVariable() and
|
||||
store = src.asInstruction() and
|
||||
storeIntoGlobal(store, var) and
|
||||
// Setting `allowFromArg` to `true` like in the base case means we
|
||||
// treat a store to a global variable like the dispatch itself: flow
|
||||
// may come from anywhere.
|
||||
allowFromArg = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate storeIntoGlobal(StoreInstruction store, GlobalOrNamespaceVariable var) {
|
||||
addressOfGlobal(store.getDestinationAddress(), var)
|
||||
}
|
||||
|
||||
/** Holds if `addressInstr` is an instruction that produces the address of `var`. */
|
||||
private predicate addressOfGlobal(Instruction addressInstr, GlobalOrNamespaceVariable var) {
|
||||
// Access directly to the global variable
|
||||
addressInstr.(VariableAddressInstruction).getAstVariable() = var
|
||||
or
|
||||
// Access to a field on a global union
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa = addressInstr and
|
||||
fa.getObjectAddress().(VariableAddressInstruction).getAstVariable() = var and
|
||||
fa.getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A ReturnNode with its ReturnKind and its enclosing callable.
|
||||
*
|
||||
* Used to fix a join ordering issue in flowsFrom.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate returnNodeWithKindAndEnclosingCallable(
|
||||
ReturnNode node, ReturnKind kind, DataFlowCallable callable
|
||||
) {
|
||||
node.getKind() = kind and
|
||||
node.getEnclosingCallable() = callable
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
private class DataSensitiveExprCall extends DataSensitiveCall {
|
||||
DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) }
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getCallTarget() }
|
||||
|
||||
override Function resolve() {
|
||||
exists(FunctionInstruction fi |
|
||||
this.flowsFrom(DataFlow::instructionNode(fi), _) and
|
||||
result = fi.getFunctionSymbol()
|
||||
) and
|
||||
(
|
||||
this.getNumberOfArguments() <= result.getEffectiveNumberOfParameters() and
|
||||
this.getNumberOfArguments() >= result.getEffectiveNumberOfParameters()
|
||||
or
|
||||
result.isVarargs()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Call to a virtual function. */
|
||||
private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(this.getStaticCallTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
}
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getThisArgument() }
|
||||
|
||||
override MemberFunction resolve() {
|
||||
exists(Class overridingClass |
|
||||
this.overrideMayAffectCall(overridingClass, result) and
|
||||
this.hasFlowFromCastFrom(overridingClass)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `this` is a virtual function call whose static target is
|
||||
* overridden by `overridingFunction` in `overridingClass`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) {
|
||||
overridingFunction.getAnOverriddenFunction+() = this.getStaticCallTarget().(VirtualFunction) and
|
||||
overridingFunction.getDeclaringType() = overridingClass
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the qualifier of `this` has flow from an upcast from
|
||||
* `derivedClass`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate hasFlowFromCastFrom(Class derivedClass) {
|
||||
exists(ConvertToBaseInstruction toBase |
|
||||
this.flowsFrom(DataFlow::instructionNode(toBase), _) and
|
||||
derivedClass = toBase.getDerivedClass()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is a function with a body that has name `qualifiedName` and
|
||||
* `nparams` parameter count. See `functionSignature`.
|
||||
*/
|
||||
private predicate functionSignatureWithBody(string qualifiedName, int nparams, Function f) {
|
||||
functionSignature(f, qualifiedName, nparams) and
|
||||
exists(f.getBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the target of `call` is a function _with no definition_ that has
|
||||
* name `qualifiedName` and `nparams` parameter count. See `functionSignature`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate callSignatureWithoutBody(string qualifiedName, int nparams, CallInstruction call) {
|
||||
exists(Function target |
|
||||
target = call.getStaticCallTarget() and
|
||||
not exists(target.getBlock()) and
|
||||
functionSignature(target, qualifiedName, nparams)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
|
||||
* an approximation of its signature for the purpose of matching functions that
|
||||
* might be the same across link targets.
|
||||
*/
|
||||
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
|
||||
qualifiedName = f.getQualifiedName() and
|
||||
nparams = f.getNumberOfParameters() and
|
||||
not f.isStatic()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(CallInstruction call, Function f) {
|
||||
mayBenefitFromCallContext(call, f, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is a call through a function pointer, and the pointer
|
||||
* value is given as the `arg`'th argument to `f`.
|
||||
*/
|
||||
private predicate mayBenefitFromCallContext(
|
||||
VirtualDispatch::DataSensitiveCall call, Function f, int arg
|
||||
) {
|
||||
f = pragma[only_bind_out](call).getEnclosingCallable() and
|
||||
exists(InitializeParameterInstruction init |
|
||||
not exists(call.getStaticCallTarget()) and
|
||||
init.getEnclosingFunction() = f and
|
||||
call.flowsFrom(DataFlow::instructionNode(init), _) and
|
||||
init.getParameter().getIndex() = arg
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
|
||||
result = viableCallable(call) and
|
||||
exists(int i, Function f |
|
||||
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
|
||||
f = ctx.getStaticCallTarget() and
|
||||
result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,235 +0,0 @@
|
||||
/**
|
||||
* Provides consistency queries for checking invariants in the language-specific
|
||||
* data-flow classes and predicates.
|
||||
*/
|
||||
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
private import tainttracking1.TaintTrackingParameter::Private
|
||||
private import tainttracking1.TaintTrackingParameter::Public
|
||||
|
||||
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() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `postHasUniquePre`. */
|
||||
predicate postHasUniquePreExclude(PostUpdateNode n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniquePostUpdate`. */
|
||||
predicate uniquePostUpdateExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `(call, ctx)` should be excluded from the consistency test `viableImplInCallContextTooLargeExclude`. */
|
||||
predicate viableImplInCallContextTooLargeExclude(
|
||||
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
RelevantNode() {
|
||||
this instanceof ArgumentNode or
|
||||
this instanceof ParameterNode or
|
||||
this instanceof ReturnNode or
|
||||
this = getAnOutNode(_, _) or
|
||||
simpleLocalFlowStep(this, _) or
|
||||
simpleLocalFlowStep(_, this) or
|
||||
jumpStep(this, _) or
|
||||
jumpStep(_, this) or
|
||||
storeStep(this, _, _) or
|
||||
storeStep(_, _, this) or
|
||||
readStep(this, _, _) or
|
||||
readStep(_, _, this) or
|
||||
defaultAdditionalTaintStep(this, _) or
|
||||
defaultAdditionalTaintStep(_, this)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueType(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(getNodeType(n)) and
|
||||
c != 1 and
|
||||
msg = "Node should have one type but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueNodeLocation(Node n, string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingLocation(string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueNodeToString(Node n, string msg) {
|
||||
exists(int c |
|
||||
c = count(n.toString()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one toString but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingToString(string msg) {
|
||||
exists(int c |
|
||||
c = strictcount(Node n | not exists(n.toString())) and
|
||||
msg = "Nodes without toString: " + c
|
||||
)
|
||||
}
|
||||
|
||||
query predicate parameterCallable(ParameterNode p, string msg) {
|
||||
exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
|
||||
msg = "Callable mismatch for parameter."
|
||||
}
|
||||
|
||||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
||||
simpleLocalFlowStep(n1, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getNodeType(_) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
t = typeRepr() and
|
||||
not compatibleTypes(t, t) and
|
||||
msg = "Type compatibility predicate is not reflexive."
|
||||
}
|
||||
|
||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||
isUnreachableInCall(n, call) and
|
||||
exists(DataFlowCallable c |
|
||||
c = nodeGetEnclosingCallable(n) and
|
||||
not viableCallable(call) = c
|
||||
) and
|
||||
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
||||
}
|
||||
|
||||
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
|
||||
(
|
||||
n = getAnOutNode(call, _) and
|
||||
msg = "OutNode and call does not share enclosing callable."
|
||||
or
|
||||
n.(ArgumentNode).argumentOf(call, _) and
|
||||
msg = "ArgumentNode and call does not share enclosing callable."
|
||||
) and
|
||||
nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
|
||||
}
|
||||
|
||||
// This predicate helps the compiler forget that in some languages
|
||||
// it is impossible for a result of `getPreUpdateNode` to be an
|
||||
// instance of `PostUpdateNode`.
|
||||
private Node getPre(PostUpdateNode n) {
|
||||
result = n.getPreUpdateNode()
|
||||
or
|
||||
none()
|
||||
}
|
||||
|
||||
query predicate postIsNotPre(PostUpdateNode n, string msg) {
|
||||
getPre(n) = n and
|
||||
msg = "PostUpdateNode should not equal its pre-update node."
|
||||
}
|
||||
|
||||
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
|
||||
not any(ConsistencyConfiguration conf).postHasUniquePreExclude(n) and
|
||||
exists(int c |
|
||||
c = count(n.getPreUpdateNode()) and
|
||||
c != 1 and
|
||||
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniquePostUpdate(Node n, string msg) {
|
||||
not any(ConsistencyConfiguration conf).uniquePostUpdateExclude(n) and
|
||||
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
|
||||
msg = "Node has multiple PostUpdateNodes."
|
||||
}
|
||||
|
||||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
||||
nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
|
||||
msg = "PostUpdateNode does not share callable with its pre-update node."
|
||||
}
|
||||
|
||||
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
|
||||
|
||||
query predicate reverseRead(Node n, string msg) {
|
||||
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."
|
||||
}
|
||||
|
||||
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
|
||||
not hasPost(n) and
|
||||
not any(ConsistencyConfiguration c).argHasPostUpdateExclude(n) and
|
||||
msg = "ArgumentNode is missing PostUpdateNode."
|
||||
}
|
||||
|
||||
// This predicate helps the compiler forget that in some languages
|
||||
// it is impossible for a `PostUpdateNode` to be the target of
|
||||
// `simpleLocalFlowStep`.
|
||||
private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() }
|
||||
|
||||
query predicate postWithInFlow(Node n, string msg) {
|
||||
isPostUpdateNode(n) and
|
||||
not clearsContent(n, _) and
|
||||
simpleLocalFlowStep(_, n) and
|
||||
not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and
|
||||
msg = "PostUpdateNode should not be the target of local flow."
|
||||
}
|
||||
|
||||
query predicate viableImplInCallContextTooLarge(
|
||||
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
|
||||
) {
|
||||
callable = viableImplInCallContext(call, ctx) and
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Provides IR-specific definitions for use in the data flow library.
|
||||
*/
|
||||
module Private {
|
||||
import DataFlowPrivate
|
||||
import DataFlowDispatch
|
||||
}
|
||||
|
||||
module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
@@ -1,560 +0,0 @@
|
||||
private import cpp as Cpp
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplConsistency
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
/** 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Instance arguments (`this` pointer) and read side effects
|
||||
* on parameters are also included.
|
||||
*/
|
||||
abstract class ArgumentNode extends Node {
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument to a call, or an
|
||||
* implicit `this` pointer argument.
|
||||
*/
|
||||
private class PrimaryArgumentNode extends ArgumentNode, OperandNode {
|
||||
override ArgumentOperand op;
|
||||
|
||||
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
op = call.getArgumentOperand(pos.(DirectPosition).getIndex())
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = argumentOperandToString(op) }
|
||||
}
|
||||
|
||||
private string argumentOperandToString(ArgumentOperand op) {
|
||||
exists(Expr unconverted |
|
||||
unconverted = op.getDef().getUnconvertedResultExpression() and
|
||||
result = unconverted.toString()
|
||||
)
|
||||
or
|
||||
// Certain instructions don't map to an unconverted result expression. For these cases
|
||||
// we fall back to a simpler naming scheme. This can happen in IR-generated constructors.
|
||||
not exists(op.getDef().getUnconvertedResultExpression()) and
|
||||
(
|
||||
result = "Argument " + op.(PositionalArgumentOperand).getIndex()
|
||||
or
|
||||
op instanceof ThisArgumentOperand and result = "Argument this"
|
||||
)
|
||||
}
|
||||
|
||||
private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode {
|
||||
override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
|
||||
this.getCallInstruction() = dfCall and
|
||||
pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and
|
||||
pos.(IndirectionPosition).getIndirectionIndex() = super.getIndirectionIndex()
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = argumentOperandToString(this.getAddressOperand()) + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition = Position;
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition = Position;
|
||||
|
||||
class Position extends TPosition {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class DirectPosition extends Position, TDirectPosition {
|
||||
int index;
|
||||
|
||||
DirectPosition() { this = TDirectPosition(index) }
|
||||
|
||||
override string toString() { if index = -1 then result = "this" else result = index.toString() }
|
||||
|
||||
int getIndex() { result = index }
|
||||
}
|
||||
|
||||
class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
int argumentIndex;
|
||||
int indirectionIndex;
|
||||
|
||||
IndirectionPosition() { this = TIndirectionPosition(argumentIndex, indirectionIndex) }
|
||||
|
||||
override string toString() {
|
||||
if argumentIndex = -1
|
||||
then if indirectionIndex > 0 then result = "this indirection" else result = "this"
|
||||
else
|
||||
if indirectionIndex > 0
|
||||
then result = argumentIndex.toString() + " indirection"
|
||||
else result = argumentIndex.toString()
|
||||
}
|
||||
|
||||
int getArgumentIndex() { result = argumentIndex }
|
||||
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
}
|
||||
|
||||
newtype TPosition =
|
||||
TDirectPosition(int index) { exists(any(CallInstruction c).getArgument(index)) } or
|
||||
TIndirectionPosition(int argumentIndex, int indirectionIndex) {
|
||||
hasOperandAndIndex(_, any(CallInstruction call).getArgumentOperand(argumentIndex),
|
||||
indirectionIndex)
|
||||
}
|
||||
|
||||
private newtype TReturnKind =
|
||||
TNormalReturnKind(int index) {
|
||||
exists(IndirectReturnNode return |
|
||||
return.getAddressOperand() = any(ReturnValueInstruction r).getReturnAddressOperand() and
|
||||
index = return.getIndirectionIndex() - 1 // We subtract one because the return loads the value.
|
||||
)
|
||||
} or
|
||||
TIndirectReturnKind(int argumentIndex, int indirectionIndex) {
|
||||
exists(IndirectReturnNode return, ReturnIndirectionInstruction returnInd |
|
||||
returnInd.hasIndex(argumentIndex) and
|
||||
return.getAddressOperand() = returnInd.getSourceAddressOperand() and
|
||||
indirectionIndex = return.getIndirectionIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A return kind. A return kind describes how a value can be returned
|
||||
* from a callable. For C++, this is simply a function return.
|
||||
*/
|
||||
class ReturnKind extends TReturnKind {
|
||||
/** Gets a textual representation of this return kind. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
private class NormalReturnKind extends ReturnKind, TNormalReturnKind {
|
||||
int index;
|
||||
|
||||
NormalReturnKind() { this = TNormalReturnKind(index) }
|
||||
|
||||
override string toString() { result = "indirect return" }
|
||||
}
|
||||
|
||||
private class IndirectReturnKind extends ReturnKind, TIndirectReturnKind {
|
||||
int argumentIndex;
|
||||
int indirectionIndex;
|
||||
|
||||
IndirectReturnKind() { this = TIndirectReturnKind(argumentIndex, indirectionIndex) }
|
||||
|
||||
override string toString() { result = "indirect outparam[" + argumentIndex.toString() + "]" }
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends Node instanceof IndirectReturnNode {
|
||||
/** Gets the kind of this returned value. */
|
||||
abstract ReturnKind getKind();
|
||||
}
|
||||
|
||||
/**
|
||||
* This predicate represents an annoying hack that we have to do. We use the
|
||||
* `ReturnIndirectionInstruction` to determine which variables need flow back
|
||||
* out of a function. However, the IR will unconditionally create those for a
|
||||
* variable passed to a function even though the variable was never updated by
|
||||
* the function. And if a function has too many `ReturnNode`s the dataflow
|
||||
* library lowers its precision for that function by disabling field flow.
|
||||
*
|
||||
* So we those eliminate `ReturnNode`s that would have otherwise been created
|
||||
* by this unconditional `ReturnIndirectionInstruction` by requiring that there
|
||||
* must exist an SSA definition of the IR variable in the function.
|
||||
*/
|
||||
private predicate hasNonInitializeParameterDef(IRVariable v) {
|
||||
exists(Ssa::Def def |
|
||||
not def.getDefiningInstruction() instanceof InitializeParameterInstruction and
|
||||
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable()
|
||||
)
|
||||
}
|
||||
|
||||
class ReturnIndirectionNode extends IndirectReturnNode, ReturnNode {
|
||||
override ReturnKind getKind() {
|
||||
exists(int argumentIndex, ReturnIndirectionInstruction returnInd |
|
||||
returnInd.hasIndex(argumentIndex) and
|
||||
this.getAddressOperand() = returnInd.getSourceAddressOperand() and
|
||||
result = TIndirectReturnKind(argumentIndex, this.getIndirectionIndex()) and
|
||||
hasNonInitializeParameterDef(returnInd.getIRVariable())
|
||||
)
|
||||
or
|
||||
this.getAddressOperand() = any(ReturnValueInstruction r).getReturnAddressOperand() and
|
||||
result = TNormalReturnKind(this.getIndirectionIndex() - 1)
|
||||
}
|
||||
}
|
||||
|
||||
private Operand fullyConvertedCallStep(Operand op) {
|
||||
not exists(getANonConversionUse(op)) and
|
||||
exists(Instruction instr |
|
||||
conversionFlow(op, instr, _) and
|
||||
result = getAUse(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that uses this operand, if the instruction is not
|
||||
* ignored for dataflow purposes.
|
||||
*/
|
||||
private Instruction getUse(Operand op) {
|
||||
result = op.getUse() and
|
||||
not Ssa::ignoreOperand(op)
|
||||
}
|
||||
|
||||
/** Gets a use of the instruction `instr` that is not ignored for dataflow purposes. */
|
||||
Operand getAUse(Instruction instr) {
|
||||
result = instr.getAUse() and
|
||||
not Ssa::ignoreOperand(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a use of `operand` that is:
|
||||
* - not ignored for dataflow purposes, and
|
||||
* - not a conversion-like instruction.
|
||||
*/
|
||||
private Instruction getANonConversionUse(Operand operand) {
|
||||
result = getUse(operand) and
|
||||
not conversionFlow(_, result, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the first use of the value of `call` following
|
||||
* a sequnce of conversion-like instructions.
|
||||
*/
|
||||
predicate operandForfullyConvertedCall(Operand operand, CallInstruction call) {
|
||||
exists(getANonConversionUse(operand)) and
|
||||
(
|
||||
operand = getAUse(call)
|
||||
or
|
||||
operand = fullyConvertedCallStep*(getAUse(call))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that represents the first use of the value of `call` following
|
||||
* a sequnce of conversion-like instructions.
|
||||
*
|
||||
* This predicate only holds if there is no suitable operand (i.e., no operand of a non-
|
||||
* conversion instruction) to use to represent the value of `call` after conversions.
|
||||
*/
|
||||
predicate instructionForfullyConvertedCall(Instruction instr, CallInstruction call) {
|
||||
not operandForfullyConvertedCall(_, call) and
|
||||
(
|
||||
// If there is no use of the call then we pick the call instruction
|
||||
not exists(getAUse(call)) and
|
||||
instr = call
|
||||
or
|
||||
// Otherwise, flow to the first non-conversion use.
|
||||
exists(Operand operand | operand = fullyConvertedCallStep*(getAUse(call)) |
|
||||
instr = getANonConversionUse(operand)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` represents the output node for `call`. */
|
||||
private predicate simpleOutNode(Node node, CallInstruction call) {
|
||||
operandForfullyConvertedCall(node.asOperand(), call)
|
||||
or
|
||||
instructionForfullyConvertedCall(node.asInstruction(), call)
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends Node {
|
||||
OutNode() {
|
||||
// Return values not hidden behind indirections
|
||||
simpleOutNode(this, _)
|
||||
or
|
||||
// Return values hidden behind indirections
|
||||
this instanceof IndirectReturnOutNode
|
||||
or
|
||||
// Modified arguments hidden behind indirections
|
||||
this instanceof IndirectArgumentOutNode
|
||||
}
|
||||
|
||||
/** Gets the underlying call. */
|
||||
abstract DataFlowCall getCall();
|
||||
|
||||
abstract ReturnKind getReturnKind();
|
||||
}
|
||||
|
||||
private class DirectCallOutNode extends OutNode {
|
||||
CallInstruction call;
|
||||
|
||||
DirectCallOutNode() { simpleOutNode(this, call) }
|
||||
|
||||
override DataFlowCall getCall() { result = call }
|
||||
|
||||
override ReturnKind getReturnKind() { result = TNormalReturnKind(0) }
|
||||
}
|
||||
|
||||
private class IndirectCallOutNode extends OutNode, IndirectReturnOutNode {
|
||||
override DataFlowCall getCall() { result = this.getCallInstruction() }
|
||||
|
||||
override ReturnKind getReturnKind() { result = TNormalReturnKind(this.getIndirectionIndex()) }
|
||||
}
|
||||
|
||||
private class SideEffectOutNode extends OutNode, IndirectArgumentOutNode {
|
||||
override DataFlowCall getCall() { result = this.getCallInstruction() }
|
||||
|
||||
override ReturnKind getReturnKind() {
|
||||
result = TIndirectReturnKind(this.getArgumentIndex(), this.getIndirectionIndex())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that can read the value returned from `call` with return kind
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result.getCall() = call and
|
||||
result.getReturnKind() = kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
exists(Cpp::GlobalOrNamespaceVariable v |
|
||||
v =
|
||||
n1.asInstruction()
|
||||
.(StoreInstruction)
|
||||
.getResultAddress()
|
||||
.(VariableAddressInstruction)
|
||||
.getAstVariable() and
|
||||
v = n2.asVariable()
|
||||
or
|
||||
v =
|
||||
n2.asInstruction()
|
||||
.(LoadInstruction)
|
||||
.getSourceAddress()
|
||||
.(VariableAddressInstruction)
|
||||
.getAstVariable() and
|
||||
v = n1.asVariable()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
|
||||
exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store |
|
||||
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
|
||||
node2.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(),
|
||||
numberOfLoads)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = node2.getUpdatedField() and
|
||||
fc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = node2.getUpdatedField() and
|
||||
uc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
|
||||
* operations and exactly `n` `LoadInstruction` operations.
|
||||
*/
|
||||
private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand operandTo, int ind) {
|
||||
exists(LoadInstruction load | load.getSourceAddressOperand() = operandFrom |
|
||||
operandTo = operandFrom and ind = 0
|
||||
or
|
||||
numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1)
|
||||
)
|
||||
or
|
||||
exists(Operand op, Instruction instr |
|
||||
instr = op.getDef() and
|
||||
conversionFlow(operandFrom, instr, _) and
|
||||
numberOfLoadsFromOperand(op, operandTo, ind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
|
||||
* operations and exactly `n` `LoadInstruction` operations.
|
||||
*/
|
||||
private predicate numberOfLoadsFromOperand(Operand operandFrom, Operand operandTo, int n) {
|
||||
numberOfLoadsFromOperandRec(operandFrom, operandTo, n)
|
||||
or
|
||||
not any(LoadInstruction load).getSourceAddressOperand() = operandFrom and
|
||||
not conversionFlow(operandFrom, _, _) and
|
||||
operandFrom = operandTo and
|
||||
n = 0
|
||||
}
|
||||
|
||||
// Needed to join on both an operand and an index at the same time.
|
||||
pragma[noinline]
|
||||
predicate nodeHasOperand(Node node, Operand operand, int indirectionIndex) {
|
||||
node.asOperand() = operand and indirectionIndex = 0
|
||||
or
|
||||
hasOperandAndIndex(node, operand, indirectionIndex)
|
||||
}
|
||||
|
||||
// Needed to join on both an instruction and an index at the same time.
|
||||
pragma[noinline]
|
||||
predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex) {
|
||||
node.asInstruction() = instr and indirectionIndex = 0
|
||||
or
|
||||
hasInstructionAndIndex(node, instr, indirectionIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content c, Node node2) {
|
||||
exists(FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2 |
|
||||
nodeHasOperand(node2, operand, indirectionIndex2) and
|
||||
nodeHasOperand(node1, fa1.getObjectAddressOperand(), _) and
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = fa1.getField() and
|
||||
fc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = fa1.getField() and
|
||||
uc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`.
|
||||
*/
|
||||
predicate clearsContent(Node n, Content c) {
|
||||
none() // stub implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
*/
|
||||
predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
DataFlowType getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
result instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getNodeType`. */
|
||||
string ppReprType(DataFlowType t) { none() } // stub implementation
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
|
||||
any() // stub implementation
|
||||
}
|
||||
|
||||
private predicate suppressUnusedNode(Node n) { any() }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that may contain code or a variable that may contain itself. When
|
||||
* flow crosses from one _enclosing callable_ to another, the interprocedural
|
||||
* data-flow library discards call contexts and inserts a node in the big-step
|
||||
* relation used for human-readable path explanations.
|
||||
*/
|
||||
class DataFlowCallable = Cpp::Declaration;
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
class DataFlowType = Type;
|
||||
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends CallInstruction {
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if access paths with `c` at their head always should be tracked at high
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c) { none() }
|
||||
|
||||
/** The unit type. */
|
||||
private newtype TUnit = TMkUnit()
|
||||
|
||||
/** The trivial type with a single element. */
|
||||
class Unit extends TUnit {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "unit" }
|
||||
}
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { n instanceof OperandNode and not n instanceof ArgumentNode }
|
||||
|
||||
class LambdaCallKind = Unit;
|
||||
|
||||
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
|
||||
|
||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
||||
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
*
|
||||
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
|
||||
* by default as a heuristic.
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
|
||||
override predicate argHasPostUpdateExclude(ArgumentNode n) {
|
||||
// The rules for whether an IR argument gets a post-update node are too
|
||||
// complex to model here.
|
||||
any()
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Provides predicates for mapping the `FunctionInput` and `FunctionOutput`
|
||||
* classes used in function models to the corresponding instructions.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
/**
|
||||
* Gets the instruction that goes into `input` for `call`.
|
||||
*/
|
||||
DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
|
||||
// An argument or qualifier
|
||||
exists(int index |
|
||||
result.asOperand() = call.getArgumentOperand(index) and
|
||||
input.isParameterOrQualifierAddress(index)
|
||||
)
|
||||
or
|
||||
// A value pointed to by an argument or qualifier
|
||||
exists(int index, int indirectionIndex |
|
||||
hasOperandAndIndex(result, call.getArgumentOperand(index), indirectionIndex) and
|
||||
input.isParameterDerefOrQualifierObject(index, indirectionIndex)
|
||||
)
|
||||
or
|
||||
exists(int ind |
|
||||
result = getIndirectReturnOutNode(call, ind) and
|
||||
input.isReturnValueDeref(ind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that holds the `output` for `call`.
|
||||
*/
|
||||
Node callOutput(CallInstruction call, FunctionOutput output) {
|
||||
// The return value
|
||||
result.asInstruction() = call and
|
||||
output.isReturnValue()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(int index, int indirectionIndex |
|
||||
result.(IndirectArgumentOutNode).getArgumentIndex() = index and
|
||||
result.(IndirectArgumentOutNode).getIndirectionIndex() = indirectionIndex and
|
||||
result.(IndirectArgumentOutNode).getCallInstruction() = call and
|
||||
output.isParameterDerefOrQualifierObject(index, indirectionIndex)
|
||||
)
|
||||
or
|
||||
exists(int ind |
|
||||
result = getIndirectReturnOutNode(call, ind) and
|
||||
output.isReturnValueDeref(ind)
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::Node callInput(CallInstruction call, FunctionInput input, int d) {
|
||||
exists(DataFlow::Node n | n = callInput(call, input) and d > 0 |
|
||||
// An argument or qualifier
|
||||
hasOperandAndIndex(result, n.asOperand(), d)
|
||||
or
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
// A value pointed to by an argument or qualifier
|
||||
hasOperandAndIndex(n, operand, indirectionIndex) and
|
||||
hasOperandAndIndex(result, operand, indirectionIndex + d)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private IndirectReturnOutNode getIndirectReturnOutNode(CallInstruction call, int d) {
|
||||
result.getCallInstruction() = call and
|
||||
result.getIndirectionIndex() = d
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that holds the `output` for `call`.
|
||||
*/
|
||||
bindingset[d]
|
||||
Node callOutput(CallInstruction call, FunctionOutput output, int d) {
|
||||
exists(DataFlow::Node n | n = callOutput(call, output) and d > 0 |
|
||||
// The return value
|
||||
result = getIndirectReturnOutNode(n.asInstruction(), d)
|
||||
or
|
||||
// If there isn't an indirect out node for the call with indirection `d` then
|
||||
// we conflate this with the underlying `CallInstruction`.
|
||||
not exists(getIndirectReturnOutNode(call, d)) and
|
||||
n.asInstruction() = result.asInstruction()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
Ssa::outNodeHasAddressAndIndex(n, operand, indirectionIndex) and
|
||||
Ssa::outNodeHasAddressAndIndex(result, operand, indirectionIndex + d)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
private import cpp
|
||||
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
|
||||
// that the cached IR gets the same checksum here as it does in queries that use
|
||||
// `ValueNumbering` without `DataFlow`.
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import PrintIRUtilities
|
||||
|
||||
/**
|
||||
* Gets the local dataflow from other nodes in the same function to this node.
|
||||
*/
|
||||
private string getFromFlow(DataFlow::Node useNode, int order1, int order2) {
|
||||
exists(DataFlow::Node defNode, string prefix |
|
||||
(
|
||||
simpleLocalFlowStep(defNode, useNode) and prefix = ""
|
||||
or
|
||||
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
|
||||
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
|
||||
prefix = "+"
|
||||
) and
|
||||
if defNode.asInstruction() = useNode.asOperand().getAnyDef()
|
||||
then
|
||||
// Shorthand for flow from the def of this operand.
|
||||
result = prefix + "def" and
|
||||
order1 = -1 and
|
||||
order2 = 0
|
||||
else
|
||||
if defNode.asOperand().getUse() = useNode.asInstruction()
|
||||
then
|
||||
// Shorthand for flow from an operand of this instruction
|
||||
result = prefix + defNode.asOperand().getDumpId() and
|
||||
order1 = -1 and
|
||||
order2 = defNode.asOperand().getDumpSortOrder()
|
||||
else result = prefix + nodeId(defNode, order1, order2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local dataflow from this node to other nodes in the same function.
|
||||
*/
|
||||
private string getToFlow(DataFlow::Node defNode, int order1, int order2) {
|
||||
exists(DataFlow::Node useNode, string prefix |
|
||||
(
|
||||
simpleLocalFlowStep(defNode, useNode) and prefix = ""
|
||||
or
|
||||
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
|
||||
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
|
||||
prefix = "+"
|
||||
) and
|
||||
if useNode.asInstruction() = defNode.asOperand().getUse()
|
||||
then
|
||||
// Shorthand for flow to this operand's instruction.
|
||||
result = prefix + "result" and
|
||||
order1 = -1 and
|
||||
order2 = 0
|
||||
else result = prefix + nodeId(useNode, order1, order2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties of the dataflow node `node`.
|
||||
*/
|
||||
private string getNodeProperty(DataFlow::Node node, string key) {
|
||||
// List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow
|
||||
// out of this node is printed as `@->dest`.
|
||||
key = "flow" and
|
||||
result =
|
||||
strictconcat(string flow, boolean to, int order1, int order2 |
|
||||
flow = getFromFlow(node, order1, order2) + "->@" and to = false
|
||||
or
|
||||
flow = "@->" + getToFlow(node, order1, order2) and to = true
|
||||
|
|
||||
flow, ", " order by to, order1, order2, flow
|
||||
)
|
||||
or
|
||||
// Is this node a dataflow sink?
|
||||
key = "sink" and
|
||||
any(DataFlow::Configuration cfg).isSink(node) and
|
||||
result = "true"
|
||||
or
|
||||
// Is this node a dataflow source?
|
||||
key = "source" and
|
||||
any(DataFlow::Configuration cfg).isSource(node) and
|
||||
result = "true"
|
||||
or
|
||||
// Is this node a dataflow barrier, and if so, what kind?
|
||||
key = "barrier" and
|
||||
result =
|
||||
strictconcat(string kind |
|
||||
any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full"
|
||||
or
|
||||
any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in"
|
||||
or
|
||||
any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out"
|
||||
|
|
||||
kind, ", "
|
||||
)
|
||||
or
|
||||
// Is there partial flow from a source to this node?
|
||||
// This property will only be emitted if partial flow is enabled by overriding
|
||||
// `DataFlow::Configration::explorationLimit()`.
|
||||
key = "pflow" and
|
||||
result =
|
||||
strictconcat(DataFlow::PartialPathNode sourceNode, DataFlow::PartialPathNode destNode, int dist,
|
||||
int order1, int order2 |
|
||||
any(DataFlow::Configuration cfg).hasPartialFlow(sourceNode, destNode, dist) and
|
||||
destNode.getNode() = node and
|
||||
// Only print flow from a source in the same function.
|
||||
sourceNode.getNode().getEnclosingCallable() = node.getEnclosingCallable()
|
||||
|
|
||||
nodeId(sourceNode.getNode(), order1, order2) + "+" + dist.toString(), ", "
|
||||
order by
|
||||
order1, order2, dist desc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Property provider for local IR dataflow.
|
||||
*/
|
||||
class LocalFlowPropertyProvider extends IRPropertyProvider {
|
||||
override string getOperandProperty(Operand operand, string key) {
|
||||
exists(DataFlow::Node node |
|
||||
operand = node.asOperand() and
|
||||
result = getNodeProperty(node, key)
|
||||
)
|
||||
}
|
||||
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
exists(DataFlow::Node node |
|
||||
instruction = node.asInstruction() and
|
||||
result = getNodeProperty(node, key)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Print the dataflow local store steps in IR dumps.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
|
||||
// that the cached IR gets the same checksum here as it does in queries that use
|
||||
// `ValueNumbering` without `DataFlow`.
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import PrintIRUtilities
|
||||
|
||||
/**
|
||||
* Property provider for local IR dataflow store steps.
|
||||
*/
|
||||
class LocalFlowPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
exists(DataFlow::Node objectNode, Content content |
|
||||
key = "content[" + content.toString() + "]" and
|
||||
instruction = objectNode.asInstruction() and
|
||||
result =
|
||||
strictconcat(string element, DataFlow::Node fieldNode |
|
||||
storeStep(fieldNode, content, objectNode) and
|
||||
element = nodeId(fieldNode, _, _)
|
||||
|
|
||||
element, ", "
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Shared utilities used when printing dataflow annotations in IR dumps.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
|
||||
// that the cached IR gets the same checksum here as it does in queries that use
|
||||
// `ValueNumbering` without `DataFlow`.
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Gets a short ID for an IR dataflow node.
|
||||
* - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`).
|
||||
* - For `Operand`s, this is the label of the operand, prefixed with the result ID of the
|
||||
* instruction and a dot (e.g. `m128.left`).
|
||||
* - For `Variable`s, this is the qualified name of the variable.
|
||||
*/
|
||||
string nodeId(DataFlow::Node node, int order1, int order2) {
|
||||
exists(Instruction instruction | instruction = node.asInstruction() |
|
||||
result = instruction.getResultId() and
|
||||
order1 = instruction.getBlock().getDisplayIndex() and
|
||||
order2 = instruction.getDisplayIndexInBlock()
|
||||
)
|
||||
or
|
||||
exists(Operand operand, Instruction instruction |
|
||||
operand = node.asOperand() and
|
||||
instruction = operand.getUse()
|
||||
|
|
||||
result = instruction.getResultId() + "." + operand.getDumpId() and
|
||||
order1 = instruction.getBlock().getDisplayIndex() and
|
||||
order2 = instruction.getDisplayIndexInBlock()
|
||||
)
|
||||
or
|
||||
result = "var(" + node.asVariable().getQualifiedName() + ")" and
|
||||
order1 = 1000000 and
|
||||
order2 = 0
|
||||
}
|
||||
@@ -1,552 +0,0 @@
|
||||
private import codeql.ssa.Ssa as SsaImplCommon
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowUtil
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import DataFlowPrivate
|
||||
private import ssa0.SsaInternals as SsaInternals0
|
||||
import SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
int getMaxIndirectionForIRVariable(IRVariable var) {
|
||||
exists(Type type, boolean isGLValue |
|
||||
var.getLanguageType().hasType(type, isGLValue) and
|
||||
if isGLValue = true
|
||||
then result = 1 + getMaxIndirectionsForType(type)
|
||||
else result = getMaxIndirectionsForType(type)
|
||||
)
|
||||
}
|
||||
|
||||
class BaseSourceVariable = SsaInternals0::BaseSourceVariable;
|
||||
|
||||
class BaseIRVariable = SsaInternals0::BaseIRVariable;
|
||||
|
||||
class BaseCallVariable = SsaInternals0::BaseCallVariable;
|
||||
|
||||
cached
|
||||
private newtype TSourceVariable =
|
||||
TSourceIRVariable(BaseIRVariable baseVar, int ind) {
|
||||
ind = [0 .. getMaxIndirectionForIRVariable(baseVar.getIRVariable())]
|
||||
} or
|
||||
TCallVariable(AllocationInstruction call, int ind) {
|
||||
ind = [0 .. countIndirectionsForCppType(getResultLanguageType(call))]
|
||||
}
|
||||
|
||||
abstract class SourceVariable extends TSourceVariable {
|
||||
int ind;
|
||||
|
||||
bindingset[ind]
|
||||
SourceVariable() { any() }
|
||||
|
||||
abstract string toString();
|
||||
|
||||
int getIndirection() { result = ind }
|
||||
|
||||
abstract BaseSourceVariable getBaseVariable();
|
||||
}
|
||||
|
||||
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
|
||||
BaseIRVariable var;
|
||||
|
||||
SourceIRVariable() { this = TSourceIRVariable(var, ind) }
|
||||
|
||||
IRVariable getIRVariable() { result = var.getIRVariable() }
|
||||
|
||||
override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
|
||||
override string toString() {
|
||||
ind = 0 and
|
||||
result = this.getIRVariable().toString()
|
||||
or
|
||||
ind > 0 and
|
||||
result = this.getIRVariable().toString() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
class CallVariable extends SourceVariable, TCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
CallVariable() { this = TCallVariable(call, ind) }
|
||||
|
||||
AllocationInstruction getCall() { result = call }
|
||||
|
||||
override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call }
|
||||
|
||||
override string toString() {
|
||||
ind = 0 and
|
||||
result = "Call"
|
||||
or
|
||||
ind > 0 and
|
||||
result = "Call indirection"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
predicate hasIndirectOperand(Operand op, int indirectionIndex) {
|
||||
exists(CppType type, int m |
|
||||
not ignoreOperand(op) and
|
||||
type = getLanguageType(op) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
indirectionIndex = [1 .. m]
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
exists(CppType type, int m |
|
||||
not ignoreInstruction(instr) and
|
||||
type = getResultLanguageType(instr) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
indirectionIndex = [1 .. m]
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefImpl(Operand address, int indirectionIndex) {
|
||||
isDef(_, _, address, _, _, indirectionIndex) and
|
||||
// We only include the definition if the SSA pruning stage
|
||||
// concluded that the definition is live after the write.
|
||||
any(SsaInternals0::Def def).getAddressOperand() = address
|
||||
} or
|
||||
TUseImpl(Operand operand, int indirectionIndex) {
|
||||
isUse(_, operand, _, _, indirectionIndex) and
|
||||
not isDef(_, _, operand, _, _, _)
|
||||
}
|
||||
|
||||
abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the block of this definition or use. */
|
||||
abstract IRBlock getBlock();
|
||||
|
||||
/** Holds if this definition or use has index `index` in block `block`. */
|
||||
abstract predicate hasIndexInBlock(IRBlock block, int index);
|
||||
|
||||
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
|
||||
this.hasIndexInBlock(block, index) and
|
||||
sv = this.getSourceVariable()
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
abstract Cpp::Location getLocation();
|
||||
|
||||
/**
|
||||
* Gets the index (i.e., the number of loads required) of this
|
||||
* definition or use.
|
||||
*
|
||||
* Note that this is _not_ the definition's (or use's) index in
|
||||
* the enclosing basic block. To obtain this index, use
|
||||
* `DefOrUseImpl::hasIndexInBlock/2` or `DefOrUseImpl::hasIndexInBlock/3`.
|
||||
*/
|
||||
abstract int getIndirectionIndex();
|
||||
|
||||
/**
|
||||
* Gets the instruction that computes the base of this definition or use.
|
||||
* This is always a `VariableAddressInstruction` or an `AllocationInstruction`.
|
||||
*/
|
||||
abstract Instruction getBase();
|
||||
|
||||
final BaseSourceVariable getBaseSourceVariable() {
|
||||
exists(IRVariable var |
|
||||
result.(BaseIRVariable).getIRVariable() = var and
|
||||
instructionHasIRVariable(this.getBase(), var)
|
||||
)
|
||||
or
|
||||
result.(BaseCallVariable).getCallInstruction() = this.getBase()
|
||||
}
|
||||
|
||||
/** Gets the variable that is defined or used. */
|
||||
final SourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v, int ind |
|
||||
sourceVariableHasBaseAndIndex(result, v, ind) and
|
||||
defOrUseHasSourceVariable(this, v, ind)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionHasIRVariable(VariableAddressInstruction vai, IRVariable var) {
|
||||
vai.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate defOrUseHasSourceVariable(DefOrUseImpl defOrUse, BaseSourceVariable bv, int ind) {
|
||||
defHasSourceVariable(defOrUse, bv, ind)
|
||||
or
|
||||
useHasSourceVariable(defOrUse, bv, ind)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defHasSourceVariable(DefImpl def, BaseSourceVariable bv, int ind) {
|
||||
bv = def.getBaseSourceVariable() and
|
||||
ind = def.getIndirection()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate useHasSourceVariable(UseImpl use, BaseSourceVariable bv, int ind) {
|
||||
bv = use.getBaseSourceVariable() and
|
||||
ind = use.getIndirection()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVariable bv, int ind) {
|
||||
v.getBaseVariable() = bv and
|
||||
v.getIndirection() = ind
|
||||
}
|
||||
|
||||
class DefImpl extends DefOrUseImpl, TDefImpl {
|
||||
Operand address;
|
||||
int ind;
|
||||
|
||||
DefImpl() { this = TDefImpl(address, ind) }
|
||||
|
||||
override Instruction getBase() { isDef(_, _, address, result, _, _) }
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
int getIndirection() { isDef(_, _, address, _, result, ind) }
|
||||
|
||||
override int getIndirectionIndex() { result = ind }
|
||||
|
||||
Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) }
|
||||
|
||||
override string toString() { result = "DefImpl" }
|
||||
|
||||
override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getDefiningInstruction() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
predicate isCertain() { isDef(true, _, address, _, _, ind) }
|
||||
}
|
||||
|
||||
class UseImpl extends DefOrUseImpl, TUseImpl {
|
||||
Operand operand;
|
||||
int ind;
|
||||
|
||||
UseImpl() { this = TUseImpl(operand, ind) }
|
||||
|
||||
Operand getOperand() { result = operand }
|
||||
|
||||
override string toString() { result = "UseImpl" }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
operand.getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
final override IRBlock getBlock() { result = operand.getUse().getBlock() }
|
||||
|
||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
||||
|
||||
final int getIndirection() { isUse(_, operand, _, result, ind) }
|
||||
|
||||
override int getIndirectionIndex() { result = ind }
|
||||
|
||||
override Instruction getBase() { isUse(_, operand, result, _, ind) }
|
||||
|
||||
predicate isCertain() { isUse(true, operand, _, _, ind) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `defOrUse1` is a definition which is first read by `use`,
|
||||
* or if `defOrUse1` is a use and `use` is a next subsequent use.
|
||||
*
|
||||
* In both cases, `use` can either be an explicit use written in the
|
||||
* source file, or it can be a phi node as computed by the SSA library.
|
||||
*/
|
||||
predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) {
|
||||
exists(IRBlock bb1, int i1, SourceVariable v |
|
||||
defOrUse1.asDefOrUse().hasIndexInBlock(bb1, i1, v)
|
||||
|
|
||||
exists(IRBlock bb2, int i2 |
|
||||
adjacentDefRead(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1),
|
||||
pragma[only_bind_into](bb2), pragma[only_bind_into](i2))
|
||||
|
|
||||
use.asDefOrUse().(UseImpl).hasIndexInBlock(bb2, i2, v)
|
||||
)
|
||||
or
|
||||
exists(PhiNode phi |
|
||||
lastRefRedef(_, bb1, i1, phi) and
|
||||
use.asPhi() = phi and
|
||||
phi.getSourceVariable() = pragma[only_bind_into](v)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate useToNode(UseOrPhi use, Node nodeTo) {
|
||||
exists(UseImpl useImpl |
|
||||
useImpl = use.asDefOrUse() and
|
||||
nodeHasOperand(nodeTo, useImpl.getOperand(), useImpl.getIndirectionIndex())
|
||||
)
|
||||
or
|
||||
nodeTo.(SsaPhiNode).getPhiNode() = use.asPhi()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate outNodeHasAddressAndIndex(
|
||||
IndirectArgumentOutNode out, Operand address, int indirectionIndex
|
||||
) {
|
||||
out.getAddressOperand() = address and
|
||||
out.getIndirectionIndex() = indirectionIndex
|
||||
}
|
||||
|
||||
private predicate defToNode(Node nodeFrom, Def def) {
|
||||
nodeHasInstruction(nodeFrom, def.getDefiningInstruction(), def.getIndirectionIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Holds if `nodeFrom` is the node that correspond to the definition or use `defOrUse`.
|
||||
*/
|
||||
predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse) {
|
||||
// Node -> Def
|
||||
defToNode(nodeFrom, defOrUse)
|
||||
or
|
||||
// Node -> Use
|
||||
useToNode(defOrUse, nodeFrom)
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a single conversion-like step from `nFrom` to `nTo`. This relation
|
||||
* only holds when there is no use-use relation out of `nTo`.
|
||||
*/
|
||||
private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
|
||||
not exists(UseOrPhi defOrUse |
|
||||
nodeToDefOrUse(nTo, defOrUse) and
|
||||
adjacentDefRead(defOrUse, _)
|
||||
) and
|
||||
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
|
||||
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and
|
||||
instr = op2.getDef() and
|
||||
conversionFlow(op1, instr, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The reason for this predicate is a bit annoying:
|
||||
* We cannot mark a `PointerArithmeticInstruction` that computes an offset based on some SSA
|
||||
* variable `x` as a use of `x` since this creates taint-flow in the following example:
|
||||
* ```c
|
||||
* int x = array[source]
|
||||
* sink(*array)
|
||||
* ```
|
||||
* This is because `source` would flow from the operand of `PointerArithmeticInstruction` to the
|
||||
* result of the instruction, and into the `IndirectOperand` that represents the value of `*array`.
|
||||
* Then, via use-use flow, flow will arrive at `*array` in `sink(*array)`.
|
||||
*
|
||||
* So this predicate recurses back along conversions and `PointerArithmeticInstruction`s to find the
|
||||
* first use that has provides use-use flow, and uses that target as the target of the `nodeFrom`.
|
||||
*/
|
||||
private predicate adjustForPointerArith(Node nodeFrom, UseOrPhi use) {
|
||||
nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and
|
||||
exists(DefOrUse defOrUse, Node adjusted |
|
||||
indirectConversionFlowStep*(adjusted, nodeFrom) and
|
||||
nodeToDefOrUse(adjusted, defOrUse) and
|
||||
adjacentDefRead(defOrUse, use)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if there is def-use or use-use flow from `nodeFrom` to `nodeTo`. */
|
||||
predicate ssaFlow(Node nodeFrom, Node nodeTo) {
|
||||
// `nodeFrom = any(PostUpdateNode pun).getPreUpdateNode()` is implied by adjustedForPointerArith.
|
||||
exists(UseOrPhi use |
|
||||
adjustForPointerArith(nodeFrom, use) and
|
||||
useToNode(use, nodeTo)
|
||||
)
|
||||
or
|
||||
not nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and
|
||||
exists(DefOrUse defOrUse1, UseOrPhi use |
|
||||
nodeToDefOrUse(nodeFrom, defOrUse1) and
|
||||
adjacentDefRead(defOrUse1, use) and
|
||||
useToNode(use, nodeTo)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `nodeTo` receives flow from the phi node `nodeFrom`. */
|
||||
predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
|
||||
exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, UseOrPhi use |
|
||||
phi = nodeFrom.getPhiNode() and
|
||||
phi.definesAt(sv, bb1, i1) and
|
||||
useToNode(use, nodeTo)
|
||||
|
|
||||
exists(IRBlock bb2, int i2 |
|
||||
use.asDefOrUse().hasIndexInBlock(bb2, i2, sv) and
|
||||
adjacentDefRead(phi, bb1, i1, bb2, i2)
|
||||
)
|
||||
or
|
||||
exists(PhiNode phiTo |
|
||||
lastRefRedef(phi, _, _, phiTo) and
|
||||
nodeTo.(SsaPhiNode).getPhiNode() = phiTo
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private SsaInternals0::SourceVariable getOldSourceVariable(SourceVariable v) {
|
||||
v.getBaseVariable().(BaseIRVariable).getIRVariable() =
|
||||
result.getBaseVariable().(SsaInternals0::BaseIRVariable).getIRVariable()
|
||||
or
|
||||
v.getBaseVariable().(BaseCallVariable).getCallInstruction() =
|
||||
result.getBaseVariable().(SsaInternals0::BaseCallVariable).getCallInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a write at index `i` in basic block `bb` to variable `v` that's
|
||||
* subsequently read (as determined by the SSA pruning stage).
|
||||
*/
|
||||
private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) {
|
||||
exists(SsaInternals0::Def def, SsaInternals0::SourceVariable v0 |
|
||||
def.asDefOrUse().hasIndexInBlock(bb, i, v0) and
|
||||
v0 = getOldSourceVariable(v)
|
||||
)
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
variableWriteCand(bb, i, v) and
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed. For C++, this is always the case.
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
|
||||
if use.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The final SSA predicates used for dataflow purposes.
|
||||
*/
|
||||
cached
|
||||
module SsaCached {
|
||||
/**
|
||||
* Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
|
||||
* or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
|
||||
* path between them without any read of `def`.
|
||||
*/
|
||||
cached
|
||||
predicate adjacentDefRead(Definition def, IRBlock bb1, int i1, IRBlock bb2, int i2) {
|
||||
SsaImpl::adjacentDefRead(def, bb1, i1, bb2, i2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
|
||||
* `def`. The reference is last because it can reach another write `next`,
|
||||
* without passing through another read or write.
|
||||
*/
|
||||
cached
|
||||
predicate lastRefRedef(Definition def, IRBlock bb, int i, Definition next) {
|
||||
SsaImpl::lastRefRedef(def, bb, i, next)
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TSsaDefOrUse =
|
||||
TDefOrUse(DefOrUseImpl defOrUse) {
|
||||
defOrUse instanceof UseImpl
|
||||
or
|
||||
// Like in the pruning stage, we only include definition that's live after the
|
||||
// write as the final definitions computed by SSA.
|
||||
exists(Definition def, SourceVariable sv, IRBlock bb, int i |
|
||||
def.definesAt(sv, bb, i) and
|
||||
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
|
||||
)
|
||||
} or
|
||||
TPhi(PhiNode phi)
|
||||
|
||||
abstract private class SsaDefOrUse extends TSsaDefOrUse {
|
||||
string toString() { none() }
|
||||
|
||||
DefOrUseImpl asDefOrUse() { none() }
|
||||
|
||||
PhiNode asPhi() { none() }
|
||||
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
class DefOrUse extends TDefOrUse, SsaDefOrUse {
|
||||
DefOrUseImpl defOrUse;
|
||||
|
||||
DefOrUse() { this = TDefOrUse(defOrUse) }
|
||||
|
||||
final override DefOrUseImpl asDefOrUse() { result = defOrUse }
|
||||
|
||||
final override Location getLocation() { result = defOrUse.getLocation() }
|
||||
|
||||
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
|
||||
|
||||
override string toString() { result = defOrUse.toString() }
|
||||
}
|
||||
|
||||
class Phi extends TPhi, SsaDefOrUse {
|
||||
PhiNode phi;
|
||||
|
||||
Phi() { this = TPhi(phi) }
|
||||
|
||||
final override PhiNode asPhi() { result = phi }
|
||||
|
||||
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
class UseOrPhi extends SsaDefOrUse {
|
||||
UseOrPhi() {
|
||||
this.asDefOrUse() instanceof UseImpl
|
||||
or
|
||||
this instanceof Phi
|
||||
}
|
||||
|
||||
final override Location getLocation() {
|
||||
result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
class Def extends DefOrUse {
|
||||
override DefImpl defOrUse;
|
||||
|
||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
||||
|
||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||
|
||||
/**
|
||||
* This predicate ensures that joins go from `defOrUse` to the result
|
||||
* instead of the other way around.
|
||||
*/
|
||||
pragma[inline]
|
||||
int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirectionIndex()
|
||||
}
|
||||
|
||||
Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
class PhiNode = SsaImpl::PhiNode;
|
||||
|
||||
class Definition = SsaImpl::Definition;
|
||||
|
||||
import SsaCached
|
||||
@@ -1,270 +0,0 @@
|
||||
import cpp as Cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
* Ignored operands are not recognizd as uses by SSA, and they don't have a
|
||||
* corresponding `(Indirect)OperandNode`.
|
||||
*/
|
||||
predicate ignoreOperand(Operand operand) {
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
|
||||
operand instanceof MemoryOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an instruction that is not used by the dataflow library.
|
||||
* Ignored instructions are not recognized as reads/writes by SSA, and they
|
||||
* don't have a corresponding `(Indirect)InstructionNode`.
|
||||
*/
|
||||
predicate ignoreInstruction(Instruction instr) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
instr instanceof WriteSideEffectInstruction or
|
||||
instr instanceof PhiInstruction or
|
||||
instr instanceof ReadSideEffectInstruction or
|
||||
instr instanceof ChiInstruction or
|
||||
instr instanceof InitializeIndirectionInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of `this` in the member function `f`.
|
||||
* The result is a glvalue if `isGLValue` is true, and
|
||||
* a prvalue if `isGLValue` is false.
|
||||
*/
|
||||
bindingset[isGLValue]
|
||||
private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) {
|
||||
result.hasType(f.getTypeOfThis(), isGLValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the instruction `i`.
|
||||
*
|
||||
* This is equivalent to `i.getResultLanguageType()` with the exception
|
||||
* of instructions that directly references a `this` IRVariable. In this
|
||||
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
|
||||
* predicate gives the expected type (i.e., a potentially cv-qualified
|
||||
* type `A*` where `A` is the declaring type of the member function that
|
||||
* contains `i`).
|
||||
*/
|
||||
cached
|
||||
CppType getResultLanguageType(Instruction i) {
|
||||
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then
|
||||
if i.isGLValue()
|
||||
then result = getThisType(i.getEnclosingFunction(), true)
|
||||
else result = getThisType(i.getEnclosingFunction(), false)
|
||||
else result = i.getResultLanguageType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the operand `operand`.
|
||||
* This is equivalent to the type of the operand's defining instruction.
|
||||
*
|
||||
* See `getResultLanguageType` for a description of this behavior.
|
||||
*/
|
||||
CppType getLanguageType(Operand operand) { result = getResultLanguageType(operand.getDef()) }
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a glvalue of type `type` can have.
|
||||
* For example:
|
||||
* - If `type = int`, the result is 1
|
||||
* - If `type = MyStruct`, the result is 1
|
||||
* - If `type = char*`, the result is 2
|
||||
*/
|
||||
int getMaxIndirectionsForType(Type type) {
|
||||
result = countIndirectionsForCppType(getTypeForGLValue(type))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a value of type `type` can have.
|
||||
*
|
||||
* Note that this predicate is intended to be called on unspecified types
|
||||
* (i.e., `countIndirections(e.getUnspecifiedType())`).
|
||||
*/
|
||||
private int countIndirections(Type t) {
|
||||
result =
|
||||
1 +
|
||||
countIndirections([t.(Cpp::PointerType).getBaseType(), t.(Cpp::ReferenceType).getBaseType()])
|
||||
or
|
||||
not t instanceof Cpp::PointerType and
|
||||
not t instanceof Cpp::ReferenceType and
|
||||
result = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a value of C++
|
||||
* type `langType` can have.
|
||||
*/
|
||||
int countIndirectionsForCppType(LanguageType langType) {
|
||||
exists(Type type | langType.hasType(type, true) |
|
||||
result = 1 + countIndirections(type.getUnspecifiedType())
|
||||
)
|
||||
or
|
||||
exists(Type type | langType.hasType(type, false) |
|
||||
result = countIndirections(type.getUnspecifiedType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CallInstruction` that calls an allocation function such
|
||||
* as `malloc` or `operator new`.
|
||||
*/
|
||||
class AllocationInstruction extends CallInstruction {
|
||||
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i` is a base instruction that starts a sequence of uses
|
||||
* of some variable that SSA can handle.
|
||||
*
|
||||
* This is either when `i` is a `VariableAddressInstruction` or when
|
||||
* `i` is a fresh allocation produced by an `AllocationInstruction`.
|
||||
*/
|
||||
private predicate isSourceVariableBase(Instruction i) {
|
||||
i instanceof VariableAddressInstruction or i instanceof AllocationInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value pointed to by `operand` can potentially be
|
||||
* modified be the caller.
|
||||
*/
|
||||
predicate isModifiableByCall(ArgumentOperand operand) {
|
||||
exists(CallInstruction call, int index, CppType type |
|
||||
type = getLanguageType(operand) and
|
||||
call.getArgumentOperand(index) = operand and
|
||||
if index = -1
|
||||
then not call.getStaticCallTarget() instanceof Cpp::ConstMemberFunction
|
||||
else not SideEffects::isConstPointerLike(any(Type t | type.hasType(t, _)))
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `op` is a use of an SSA variable rooted at `base` with `ind` number
|
||||
* of indirections.
|
||||
*
|
||||
* `certain` is `true` if the operand is guaranteed to read the variable, and
|
||||
* `indirectionIndex` specifies the number of loads required to read the variable.
|
||||
*/
|
||||
cached
|
||||
predicate isUse(boolean certain, Operand op, Instruction base, int ind, int indirectionIndex) {
|
||||
not ignoreOperand(op) and
|
||||
certain = true and
|
||||
exists(LanguageType type, int m, int ind0 |
|
||||
type = getLanguageType(op) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
isUseImpl(op, base, ind0) and
|
||||
ind = ind0 + [0 .. m] and
|
||||
indirectionIndex = ind - ind0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operand` is a use of an SSA variable rooted at `base`, and the
|
||||
* path from `base` to `operand` passes through `ind` load-like instructions.
|
||||
*/
|
||||
private predicate isUseImpl(Operand operand, Instruction base, int ind) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
ind = 0 and
|
||||
operand.getDef() = base and
|
||||
isSourceVariableBase(base)
|
||||
or
|
||||
exists(Operand mid, Instruction instr |
|
||||
isUseImpl(mid, base, ind) and
|
||||
instr = operand.getDef() and
|
||||
conversionFlow(mid, instr, false)
|
||||
)
|
||||
or
|
||||
exists(int ind0 |
|
||||
isUseImpl(operand.getDef().(LoadInstruction).getSourceAddressOperand(), base, ind0)
|
||||
or
|
||||
isUseImpl(operand.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind0)
|
||||
|
|
||||
ind0 = ind - 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `address` is an address of an SSA variable rooted at `base`,
|
||||
* and `instr` is a definition of the SSA variable with `ind` number of indirections.
|
||||
*
|
||||
* `certain` is `true` if `instr` is guaranteed to write to the variable, and
|
||||
* `indirectionIndex` specifies the number of loads required to read the variable
|
||||
* after the write operation.
|
||||
*/
|
||||
cached
|
||||
predicate isDef(
|
||||
boolean certain, Instruction instr, Operand address, Instruction base, int ind,
|
||||
int indirectionIndex
|
||||
) {
|
||||
certain = true and
|
||||
exists(int ind0, CppType type, int m |
|
||||
address =
|
||||
[
|
||||
instr.(StoreInstruction).getDestinationAddressOperand(),
|
||||
instr.(InitializeParameterInstruction).getAnOperand(),
|
||||
instr.(InitializeDynamicAllocationInstruction).getAllocationAddressOperand(),
|
||||
instr.(UninitializedInstruction).getAnOperand()
|
||||
]
|
||||
|
|
||||
isDefImpl(address, base, ind0) and
|
||||
type = getLanguageType(address) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
ind = ind0 + [1 .. m] and
|
||||
indirectionIndex = ind - (ind0 + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `address` is a use of an SSA variable rooted at `base`, and the
|
||||
* path from `base` to `address` passes through `ind` load-like instructions.
|
||||
*
|
||||
* Note: Unlike `isUseImpl`, this predicate recurses through pointer-arithmetic
|
||||
* instructions.
|
||||
*/
|
||||
private predicate isDefImpl(Operand address, Instruction base, int ind) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
ind = 0 and
|
||||
address.getDef() = base and
|
||||
isSourceVariableBase(base)
|
||||
or
|
||||
exists(Operand mid, Instruction instr |
|
||||
isDefImpl(mid, base, ind) and
|
||||
instr = address.getDef() and
|
||||
conversionFlow(mid, instr, _)
|
||||
)
|
||||
or
|
||||
exists(int ind0 |
|
||||
isDefImpl(address.getDef().(LoadInstruction).getSourceAddressOperand(), base, ind0)
|
||||
or
|
||||
isDefImpl(address.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind0)
|
||||
|
|
||||
ind0 = ind - 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Inputs to the shared SSA library's parameterized module that is shared
|
||||
* between the SSA pruning stage, and the final SSA stage.
|
||||
*/
|
||||
module InputSigCommon {
|
||||
class BasicBlock = IRBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends IRBlock {
|
||||
ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import ModelUtil
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.cpp.models.Models
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
|
||||
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||
or
|
||||
modeledTaintStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow from `op` to `*op`.
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, operand, indirectionIndex) and
|
||||
nodeHasOperand(nodeTo, operand, indirectionIndex - 1)
|
||||
)
|
||||
or
|
||||
// Flow from `instr` to `*instr`.
|
||||
exists(Instruction instr, int indirectionIndex |
|
||||
nodeHasInstruction(nodeFrom, instr, indirectionIndex) and
|
||||
nodeHasInstruction(nodeTo, instr, indirectionIndex - 1)
|
||||
)
|
||||
or
|
||||
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
|
||||
// indirection of the pointer arithmetic instruction. This provides flow from `source`
|
||||
// in `x[source]` to the result of the associated load instruction.
|
||||
exists(PointerArithmeticInstruction pai, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
private predicate operandToInstructionTaintStep(Operand opFrom, Instruction instrTo) {
|
||||
// Taint can flow through expressions that alter the value but preserve
|
||||
// more than one bit of it _or_ expressions that follow data through
|
||||
// pointer indirections.
|
||||
instrTo.getAnOperand() = opFrom and
|
||||
(
|
||||
instrTo instanceof ArithmeticInstruction
|
||||
or
|
||||
instrTo instanceof BitwiseInstruction
|
||||
or
|
||||
instrTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// The `CopyInstruction` case is also present in non-taint data flow, but
|
||||
// that uses `getDef` rather than `getAnyDef`. For taint, we want flow
|
||||
// from a definition of `myStruct` to a `myStruct.myField` expression.
|
||||
instrTo.(LoadInstruction).getSourceAddressOperand() = opFrom
|
||||
or
|
||||
// Unary instructions tend to preserve enough information in practice that we
|
||||
// want taint to flow through.
|
||||
// The exception is `FieldAddressInstruction`. Together with the rules below for
|
||||
// `LoadInstruction`s and `ChiInstruction`s, flow through `FieldAddressInstruction`
|
||||
// could cause flow into one field to come out an unrelated field.
|
||||
// This would happen across function boundaries, where the IR would not be able to
|
||||
// match loads to stores.
|
||||
instrTo.(UnaryInstruction).getUnaryOperand() = opFrom and
|
||||
(
|
||||
not instrTo instanceof FieldAddressInstruction
|
||||
or
|
||||
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localInstructionTaint(Instruction i1, Instruction i2) {
|
||||
localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprTaint(Expr e1, Expr e2) {
|
||||
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `src` to `sink` should be included in all
|
||||
* global taint flow configurations.
|
||||
*/
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintStep(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if default `TaintTracking::Configuration`s should allow implicit reads
|
||||
* of `c` at sinks and inputs to additional taint steps.
|
||||
*/
|
||||
bindingset[node]
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
|
||||
* modeled function.
|
||||
*/
|
||||
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
|
||||
// Normal taint steps
|
||||
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasTaintFlow(modelIn, modelOut)
|
||||
|
|
||||
(
|
||||
nodeIn = callInput(call, modelIn)
|
||||
or
|
||||
exists(int n |
|
||||
modelIn.isParameterDerefOrQualifierObject(n) and
|
||||
if n = -1
|
||||
then nodeIn = callInput(call, any(InQualifierAddress inQualifier))
|
||||
else nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
|
||||
)
|
||||
) and
|
||||
nodeOut = callOutput(call, modelOut)
|
||||
or
|
||||
exists(int d |
|
||||
nodeIn = callInput(call, modelIn, d)
|
||||
or
|
||||
exists(int n |
|
||||
d = 1 and
|
||||
modelIn.isParameterDerefOrQualifierObject(n) and
|
||||
if n = -1
|
||||
then nodeIn = callInput(call, any(InQualifierAddress inQualifier))
|
||||
else nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
|
||||
)
|
||||
|
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasTaintFlow(modelIn, modelOut) and
|
||||
nodeOut = callOutput(call, modelOut, d)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Taint flow from one argument to another and data flow from an argument to a
|
||||
// return value. This happens in functions like `strcat` and `memcpy`. We
|
||||
// could model this flow in two separate steps, but that would add reverse
|
||||
// flow from the write side-effect to the call instruction, which may not be
|
||||
// desirable.
|
||||
exists(
|
||||
CallInstruction call, Function func, FunctionInput modelIn, OutParameterDeref modelMidOut,
|
||||
int indexMid, InParameter modelMidIn, OutReturnValue modelOut
|
||||
|
|
||||
nodeIn = callInput(call, modelIn) and
|
||||
nodeOut = callOutput(call, modelOut) and
|
||||
call.getStaticCallTarget() = func and
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
|
||||
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
|
||||
modelMidOut.isParameterDeref(indexMid) and
|
||||
modelMidIn.isParameter(indexMid)
|
||||
)
|
||||
or
|
||||
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
|
||||
// to that output, but the deref is not modeled in the IR for the caller.
|
||||
exists(
|
||||
CallInstruction call, DataFlow::SideEffectOperandNode indirectArgument, Function func,
|
||||
FunctionInput modelIn, FunctionOutput modelOut
|
||||
|
|
||||
indirectArgument = callInput(call, modelIn) and
|
||||
indirectArgument.getAddressOperand() = nodeIn.asOperand() and
|
||||
call.getStaticCallTarget() = func and
|
||||
(
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
) and
|
||||
nodeOut = callOutput(call, modelOut)
|
||||
)
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
/**
|
||||
* This module defines an initial SSA pruning stage that doesn't take
|
||||
* indirections into account.
|
||||
*/
|
||||
|
||||
private import codeql.ssa.Ssa as SsaImplCommon
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
newtype TBaseSourceVariable =
|
||||
// Each IR variable gets its own source variable
|
||||
TBaseIRVariable(IRVariable var) or
|
||||
// Each allocation gets its own source variable
|
||||
TBaseCallVariable(AllocationInstruction call)
|
||||
|
||||
abstract class BaseSourceVariable extends TBaseSourceVariable {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
}
|
||||
|
||||
class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable {
|
||||
IRVariable var;
|
||||
|
||||
IRVariable getIRVariable() { result = var }
|
||||
|
||||
BaseIRVariable() { this = TBaseIRVariable(var) }
|
||||
|
||||
override string toString() { result = var.toString() }
|
||||
|
||||
override DataFlowType getType() { result = var.getType() }
|
||||
}
|
||||
|
||||
class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
BaseCallVariable() { this = TBaseCallVariable(call) }
|
||||
|
||||
AllocationInstruction getCallInstruction() { result = call }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override DataFlowType getType() { result = call.getResultType() }
|
||||
}
|
||||
|
||||
private newtype TSourceVariable =
|
||||
TSourceIRVariable(BaseIRVariable baseVar) or
|
||||
TCallVariable(AllocationInstruction call)
|
||||
|
||||
abstract class SourceVariable extends TSourceVariable {
|
||||
abstract string toString();
|
||||
|
||||
abstract BaseSourceVariable getBaseVariable();
|
||||
}
|
||||
|
||||
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
|
||||
BaseIRVariable var;
|
||||
|
||||
SourceIRVariable() { this = TSourceIRVariable(var) }
|
||||
|
||||
IRVariable getIRVariable() { result = var.getIRVariable() }
|
||||
|
||||
override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
|
||||
override string toString() { result = this.getIRVariable().toString() }
|
||||
}
|
||||
|
||||
class CallVariable extends SourceVariable, TCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
CallVariable() { this = TCallVariable(call) }
|
||||
|
||||
AllocationInstruction getCall() { result = call }
|
||||
|
||||
override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call }
|
||||
|
||||
override string toString() { result = "Call" }
|
||||
}
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefImpl(Operand address) { isDef(_, _, address, _, _, _) } or
|
||||
TUseImpl(Operand operand) {
|
||||
isUse(_, operand, _, _, _) and
|
||||
not isDef(_, _, operand, _, _, _)
|
||||
}
|
||||
|
||||
abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the block of this definition or use. */
|
||||
abstract IRBlock getBlock();
|
||||
|
||||
/** Holds if this definition or use has index `index` in block `block`. */
|
||||
abstract predicate hasIndexInBlock(IRBlock block, int index);
|
||||
|
||||
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
|
||||
this.hasIndexInBlock(block, index) and
|
||||
sv = this.getSourceVariable()
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
abstract Cpp::Location getLocation();
|
||||
|
||||
abstract Instruction getBase();
|
||||
|
||||
final BaseSourceVariable getBaseSourceVariable() {
|
||||
exists(IRVariable var |
|
||||
result.(BaseIRVariable).getIRVariable() = var and
|
||||
instructionHasIRVariable(this.getBase(), var)
|
||||
)
|
||||
or
|
||||
result.(BaseCallVariable).getCallInstruction() = this.getBase()
|
||||
}
|
||||
|
||||
/** Gets the variable that is defined or used. */
|
||||
final SourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v |
|
||||
sourceVariableHasBaseAndIndex(result, v) and
|
||||
defOrUseHasSourceVariable(this, v)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionHasIRVariable(VariableAddressInstruction vai, IRVariable var) {
|
||||
vai.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate defOrUseHasSourceVariable(DefOrUseImpl defOrUse, BaseSourceVariable bv) {
|
||||
defHasSourceVariable(defOrUse, bv)
|
||||
or
|
||||
useHasSourceVariable(defOrUse, bv)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defHasSourceVariable(DefImpl def, BaseSourceVariable bv) {
|
||||
bv = def.getBaseSourceVariable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate useHasSourceVariable(UseImpl use, BaseSourceVariable bv) {
|
||||
bv = use.getBaseSourceVariable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVariable bv) {
|
||||
v.getBaseVariable() = bv
|
||||
}
|
||||
|
||||
class DefImpl extends DefOrUseImpl, TDefImpl {
|
||||
Operand address;
|
||||
|
||||
DefImpl() { this = TDefImpl(address) }
|
||||
|
||||
override Instruction getBase() { isDef(_, _, address, result, _, _) }
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) }
|
||||
|
||||
override string toString() { result = address.toString() }
|
||||
|
||||
override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getDefiningInstruction() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
predicate isCertain() { isDef(true, _, address, _, _, _) }
|
||||
}
|
||||
|
||||
class UseImpl extends DefOrUseImpl, TUseImpl {
|
||||
Operand operand;
|
||||
|
||||
UseImpl() { this = TUseImpl(operand) }
|
||||
|
||||
Operand getOperand() { result = operand }
|
||||
|
||||
override string toString() { result = operand.toString() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
operand.getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
final override IRBlock getBlock() { result = operand.getUse().getBlock() }
|
||||
|
||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
||||
|
||||
override Instruction getBase() { isUse(_, operand, result, _, _) }
|
||||
|
||||
predicate isCertain() { isUse(true, operand, _, _, _) }
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed.
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
|
||||
if use.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TSsaDefOrUse =
|
||||
TDefOrUse(DefOrUseImpl defOrUse) {
|
||||
defOrUse instanceof UseImpl
|
||||
or
|
||||
// If `defOrUse` is a definition we only include it if the
|
||||
// SSA library concludes that it's live after the write.
|
||||
exists(Definition def, SourceVariable sv, IRBlock bb, int i |
|
||||
def.definesAt(sv, bb, i) and
|
||||
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
|
||||
)
|
||||
} or
|
||||
TPhi(PhiNode phi)
|
||||
|
||||
abstract private class SsaDefOrUse extends TSsaDefOrUse {
|
||||
string toString() { result = "SsaDefOrUse" }
|
||||
|
||||
DefOrUseImpl asDefOrUse() { none() }
|
||||
|
||||
PhiNode asPhi() { none() }
|
||||
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
class DefOrUse extends TDefOrUse, SsaDefOrUse {
|
||||
DefOrUseImpl defOrUse;
|
||||
|
||||
DefOrUse() { this = TDefOrUse(defOrUse) }
|
||||
|
||||
final override DefOrUseImpl asDefOrUse() { result = defOrUse }
|
||||
|
||||
final override Location getLocation() { result = defOrUse.getLocation() }
|
||||
|
||||
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
|
||||
}
|
||||
|
||||
class Phi extends TPhi, SsaDefOrUse {
|
||||
PhiNode phi;
|
||||
|
||||
Phi() { this = TPhi(phi) }
|
||||
|
||||
final override PhiNode asPhi() { result = phi }
|
||||
|
||||
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
||||
}
|
||||
|
||||
class UseOrPhi extends SsaDefOrUse {
|
||||
UseOrPhi() {
|
||||
this.asDefOrUse() instanceof UseImpl
|
||||
or
|
||||
this instanceof Phi
|
||||
}
|
||||
|
||||
final override Location getLocation() {
|
||||
result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = this.asDefOrUse().toString()
|
||||
or
|
||||
this instanceof Phi and
|
||||
result = "Phi"
|
||||
}
|
||||
}
|
||||
|
||||
class Def extends DefOrUse {
|
||||
override DefImpl defOrUse;
|
||||
|
||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
||||
|
||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||
|
||||
Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() }
|
||||
|
||||
override string toString() { result = this.asDefOrUse().toString() + " (def)" }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
class PhiNode = SsaImpl::PhiNode;
|
||||
|
||||
class Definition = SsaImpl::Definition;
|
||||
@@ -1,5 +0,0 @@
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as DataFlow
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow2::DataFlow2 as DataFlow
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow3::DataFlow3 as DataFlow
|
||||
}
|
||||
@@ -28,10 +28,6 @@ private newtype TBound =
|
||||
i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction
|
||||
or
|
||||
i.getAUse() instanceof ArgumentOperand
|
||||
or
|
||||
i instanceof PointerArithmeticInstruction
|
||||
or
|
||||
i.getAUse() instanceof AddressOperand
|
||||
)
|
||||
}
|
||||
|
||||
@@ -77,7 +73,7 @@ class ValueNumberBound extends Bound, TBoundValueNumber {
|
||||
this = TBoundValueNumber(valueNumber(result)) and delta = 0
|
||||
}
|
||||
|
||||
override string toString() { result = "ValueNumberBound" }
|
||||
override string toString() { result = vn.getExampleInstruction().toString() }
|
||||
|
||||
override Location getLocation() { result = vn.getLocation() }
|
||||
|
||||
|
||||
@@ -178,11 +178,11 @@ class SemRelationalExpr extends SemBinaryExpr {
|
||||
}
|
||||
|
||||
class SemAddExpr extends SemBinaryExpr {
|
||||
SemAddExpr() { opcode instanceof Opcode::Add or opcode instanceof Opcode::PointerAdd }
|
||||
SemAddExpr() { opcode instanceof Opcode::Add }
|
||||
}
|
||||
|
||||
class SemSubExpr extends SemBinaryExpr {
|
||||
SemSubExpr() { opcode instanceof Opcode::Sub or opcode instanceof Opcode::PointerSub }
|
||||
SemSubExpr() { opcode instanceof Opcode::Sub }
|
||||
}
|
||||
|
||||
class SemMulExpr extends SemBinaryExpr {
|
||||
|
||||
@@ -7,7 +7,6 @@ private import semmle.code.cpp.ir.IR as IR
|
||||
private import Semantic
|
||||
private import experimental.semmle.code.cpp.rangeanalysis.Bound as IRBound
|
||||
private import semmle.code.cpp.controlflow.IRGuards as IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
module SemanticExprConfig {
|
||||
class Location = Cpp::Location;
|
||||
@@ -113,23 +112,16 @@ module SemanticExprConfig {
|
||||
|
||||
predicate hasDominanceInformation(BasicBlock block) { any() }
|
||||
|
||||
private predicate id(Cpp::Locatable x, Cpp::Locatable y) { x = y }
|
||||
|
||||
private predicate idOf(Cpp::Locatable x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
|
||||
int getBasicBlockUniqueId(BasicBlock block) { idOf(block.getFirstInstruction().getAst(), result) }
|
||||
int getBasicBlockUniqueId(BasicBlock block) {
|
||||
// REVIEW: `getDisplayIndex()` is not intended for use in real queries, but for now it's the
|
||||
// best we can do because `equivalentRelation` won't accept a predicate whose parameters are IPA
|
||||
// types.
|
||||
result = block.getDisplayIndex()
|
||||
}
|
||||
|
||||
newtype TSsaVariable =
|
||||
TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or
|
||||
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() } or
|
||||
TSsaPointerArithmeticGuard(IR::PointerArithmeticInstruction instr) {
|
||||
exists(Guard g, IR::Operand use | use = instr.getAUse() |
|
||||
g.comparesLt(use, _, _, _, _) or
|
||||
g.comparesLt(_, use, _, _, _) or
|
||||
g.comparesEq(use, _, _, _, _) or
|
||||
g.comparesEq(_, use, _, _, _)
|
||||
)
|
||||
}
|
||||
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() }
|
||||
|
||||
class SsaVariable extends TSsaVariable {
|
||||
string toString() { none() }
|
||||
@@ -138,8 +130,6 @@ module SemanticExprConfig {
|
||||
|
||||
IR::Instruction asInstruction() { none() }
|
||||
|
||||
IR::PointerArithmeticInstruction asPointerArithGuard() { none() }
|
||||
|
||||
IR::Operand asOperand() { none() }
|
||||
}
|
||||
|
||||
@@ -155,18 +145,6 @@ module SemanticExprConfig {
|
||||
final override IR::Instruction asInstruction() { result = instr }
|
||||
}
|
||||
|
||||
class SsaPointerArithmeticGuard extends SsaVariable, TSsaPointerArithmeticGuard {
|
||||
IR::PointerArithmeticInstruction instr;
|
||||
|
||||
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(instr) }
|
||||
|
||||
final override string toString() { result = instr.toString() }
|
||||
|
||||
final override Location getLocation() { result = instr.getLocation() }
|
||||
|
||||
final override IR::PointerArithmeticInstruction asPointerArithGuard() { result = instr }
|
||||
}
|
||||
|
||||
class SsaOperand extends SsaVariable, TSsaOperand {
|
||||
IR::Operand op;
|
||||
|
||||
@@ -184,18 +162,14 @@ module SemanticExprConfig {
|
||||
predicate phi(SsaVariable v) { v.asInstruction() instanceof IR::PhiInstruction }
|
||||
|
||||
SsaVariable getAPhiInput(SsaVariable v) {
|
||||
exists(IR::PhiInstruction instr | v.asInstruction() = instr |
|
||||
exists(IR::PhiInstruction instr |
|
||||
result.asInstruction() = instr.getAnInput()
|
||||
or
|
||||
result.asOperand() = instr.getAnInputOperand()
|
||||
)
|
||||
}
|
||||
|
||||
Expr getAUse(SsaVariable v) {
|
||||
result.(IR::LoadInstruction).getSourceValue() = v.asInstruction()
|
||||
or
|
||||
result = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
||||
}
|
||||
Expr getAUse(SsaVariable v) { result.(IR::LoadInstruction).getSourceValue() = v.asInstruction() }
|
||||
|
||||
SemType getSsaVariableType(SsaVariable v) {
|
||||
result = getSemanticType(v.asInstruction().getResultIRType())
|
||||
@@ -235,9 +209,7 @@ module SemanticExprConfig {
|
||||
|
||||
final override predicate hasRead(SsaVariable v) {
|
||||
exists(IR::Operand operand |
|
||||
operand.getDef() = v.asInstruction() or
|
||||
operand.getDef() = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
||||
|
|
||||
operand.getDef() = v.asInstruction() and
|
||||
not operand instanceof IR::PhiInputOperand and
|
||||
operand.getUse().getBlock() = block
|
||||
)
|
||||
@@ -256,9 +228,7 @@ module SemanticExprConfig {
|
||||
|
||||
final override predicate hasRead(SsaVariable v) {
|
||||
exists(IR::PhiInputOperand operand |
|
||||
operand.getDef() = v.asInstruction() or
|
||||
operand.getDef() = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
||||
|
|
||||
operand.getDef() = v.asInstruction() and
|
||||
operand.getPredecessorBlock() = pred and
|
||||
operand.getUse().getBlock() = succ
|
||||
)
|
||||
@@ -297,7 +267,17 @@ module SemanticExprConfig {
|
||||
|
||||
ValueNumberBound() { bound = this }
|
||||
|
||||
override string toString() { result = bound.toString() }
|
||||
override string toString() {
|
||||
result =
|
||||
min(SsaVariable v |
|
||||
v.asInstruction() = bound.getValueNumber().getAnInstruction()
|
||||
|
|
||||
v
|
||||
order by
|
||||
v.asInstruction().getBlock().getDisplayIndex(),
|
||||
v.asInstruction().getDisplayIndexInBlock()
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
|
||||
predicate zeroBound(Bound bound) { bound instanceof IRBound::ZeroBound }
|
||||
|
||||
@@ -65,18 +65,10 @@ module Opcode {
|
||||
override string toString() { result = "Add" }
|
||||
}
|
||||
|
||||
class PointerAdd extends Opcode, TPointerAdd {
|
||||
override string toString() { result = "PointerAdd" }
|
||||
}
|
||||
|
||||
class Sub extends Opcode, TSub {
|
||||
override string toString() { result = "Sub" }
|
||||
}
|
||||
|
||||
class PointerSub extends Opcode, TPointerSub {
|
||||
override string toString() { result = "PointerSub" }
|
||||
}
|
||||
|
||||
class Mul extends Opcode, TMul {
|
||||
override string toString() { result = "Mul" }
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ class SemSsaVariable instanceof Specific::SsaVariable {
|
||||
|
||||
final Specific::Location getLocation() { result = super.getLocation() }
|
||||
|
||||
final SemExpr getAUse() { result = Specific::getAUse(this) }
|
||||
final SemLoadExpr getAUse() { result = Specific::getAUse(this) }
|
||||
|
||||
final SemType getType() { result = Specific::getSsaVariableType(this) }
|
||||
|
||||
|
||||
@@ -223,9 +223,7 @@ private SemGuard boundFlowCond(
|
||||
else resultIsStrict = testIsTrue.booleanNot()
|
||||
) and
|
||||
(
|
||||
if
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType or
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemAddressType
|
||||
if getTrackedTypeForSsaVariable(v) instanceof SemIntegerType
|
||||
then
|
||||
upper = true and strengthen = -1
|
||||
or
|
||||
@@ -544,32 +542,12 @@ private predicate unequalIntegralSsa(
|
||||
) {
|
||||
exists(SemExpr e, int d1, int d2 |
|
||||
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
|
||||
boundedUpper(e, b, d1) and
|
||||
boundedLower(e, b, d2) and
|
||||
bounded(e, b, d2, true, _, _, _) and
|
||||
bounded(e, b, d2, false, _, _, _) and
|
||||
delta = d2 + d1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is an upper bound for `e`.
|
||||
*
|
||||
* This predicate only exists to prevent a bad standard order in `unequalIntegralSsa`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedUpper(SemExpr e, SemBound b, int delta) {
|
||||
bounded(e, b, delta, true, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a lower bound for `e`.
|
||||
*
|
||||
* This predicate only exists to prevent a bad standard order in `unequalIntegralSsa`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedLower(SemExpr e, SemBound b, int delta) {
|
||||
bounded(e, b, delta, false, _, _, _)
|
||||
}
|
||||
|
||||
/** Weakens a delta to lie in the range `[-1..1]`. */
|
||||
bindingset[delta, upper]
|
||||
private int weakenDelta(boolean upper, int delta) {
|
||||
|
||||
@@ -204,7 +204,6 @@ private class BinarySignExpr extends FlowSignExpr {
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
|
||||
binary.getLeftOperand() = left and binary.getRightOperand() = right
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.4.2-dev
|
||||
version: 0.3.4-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/ssa: 0.0.1
|
||||
|
||||
@@ -404,10 +404,7 @@ class Class extends UserType {
|
||||
* compiled for. For this reason, the `is_pod_class` predicate is
|
||||
* generated by the extractor.
|
||||
*/
|
||||
predicate isPod() { is_pod_class(underlyingElement(this)) }
|
||||
|
||||
/** DEPRECATED: Alias for isPod */
|
||||
deprecated predicate isPOD() { this.isPod() }
|
||||
predicate isPOD() { is_pod_class(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this class, struct or union is a standard-layout class
|
||||
|
||||
@@ -79,17 +79,17 @@ predicate isAggregateType03(Type t) {
|
||||
* user-defined copy assignment operator and no user-defined destructor.
|
||||
* A POD class is a class that is either a POD-struct or a POD-union.
|
||||
*/
|
||||
predicate isPodClass03(Class c) {
|
||||
predicate isPODClass03(Class c) {
|
||||
isAggregateClass03(c) and
|
||||
not exists(Variable v |
|
||||
v.getDeclaringType() = c and
|
||||
not v.isStatic()
|
||||
|
|
||||
not isPodType03(v.getType())
|
||||
not isPODType03(v.getType())
|
||||
or
|
||||
exists(ArrayType at |
|
||||
at = v.getType() and
|
||||
not isPodType03(at.getBaseType())
|
||||
not isPODType03(at.getBaseType())
|
||||
)
|
||||
or
|
||||
v.getType() instanceof ReferenceType
|
||||
@@ -104,9 +104,6 @@ predicate isPodClass03(Class c) {
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for isPodClass03 */
|
||||
deprecated predicate isPODClass03 = isPodClass03/1;
|
||||
|
||||
/**
|
||||
* Holds if `t` is a POD type, according to the rules specified in
|
||||
* C++03 3.9(10):
|
||||
@@ -115,17 +112,14 @@ deprecated predicate isPODClass03 = isPodClass03/1;
|
||||
* such types and cv-qualified versions of these types (3.9.3) are
|
||||
* collectively called POD types.
|
||||
*/
|
||||
predicate isPodType03(Type t) {
|
||||
predicate isPODType03(Type t) {
|
||||
exists(Type ut | ut = t.getUnderlyingType() |
|
||||
isScalarType03(ut)
|
||||
or
|
||||
isPodClass03(ut)
|
||||
isPODClass03(ut)
|
||||
or
|
||||
exists(ArrayType at | at = ut and isPodType03(at.getBaseType()))
|
||||
exists(ArrayType at | at = ut and isPODType03(at.getBaseType()))
|
||||
or
|
||||
isPodType03(ut.(SpecifiedType).getUnspecifiedType())
|
||||
isPODType03(ut.(SpecifiedType).getUnspecifiedType())
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for isPodType03 */
|
||||
deprecated predicate isPODType03 = isPodType03/1;
|
||||
|
||||
20
cpp/ql/lib/semmle/code/cpp/XML.qll
Normal file → Executable file
20
cpp/ql/lib/semmle/code/cpp/XML.qll
Normal file → Executable file
@@ -132,10 +132,7 @@ class XmlFile extends XmlParent, File {
|
||||
XmlElement getARootElement() { result = this.getAChild() }
|
||||
|
||||
/** Gets a DTD associated with this XML file. */
|
||||
XmlDtd getADtd() { xmlDTDs(result, _, _, _, this) }
|
||||
|
||||
/** DEPRECATED: Alias for getADtd */
|
||||
deprecated XmlDtd getADTD() { result = this.getADtd() }
|
||||
XmlDTD getADTD() { xmlDTDs(result, _, _, _, this) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlFile */
|
||||
@@ -152,7 +149,7 @@ deprecated class XMLFile = XmlFile;
|
||||
* <!ELEMENT lastName (#PCDATA)>
|
||||
* ```
|
||||
*/
|
||||
class XmlDtd extends XmlLocatable, @xmldtd {
|
||||
class XmlDTD extends XmlLocatable, @xmldtd {
|
||||
/** Gets the name of the root element of this DTD. */
|
||||
string getRoot() { xmlDTDs(this, result, _, _, _) }
|
||||
|
||||
@@ -177,8 +174,8 @@ class XmlDtd extends XmlLocatable, @xmldtd {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlDtd */
|
||||
deprecated class XMLDTD = XmlDtd;
|
||||
/** DEPRECATED: Alias for XmlDTD */
|
||||
deprecated class XMLDTD = XmlDTD;
|
||||
|
||||
/**
|
||||
* An XML element in an XML file.
|
||||
@@ -285,18 +282,15 @@ class XmlNamespace extends XmlLocatable, @xmlnamespace {
|
||||
string getPrefix() { xmlNs(this, result, _, _) }
|
||||
|
||||
/** Gets the URI of this namespace. */
|
||||
string getUri() { xmlNs(this, _, result, _) }
|
||||
|
||||
/** DEPRECATED: Alias for getUri */
|
||||
deprecated string getURI() { result = this.getUri() }
|
||||
string getURI() { xmlNs(this, _, result, _) }
|
||||
|
||||
/** Holds if this namespace has no prefix. */
|
||||
predicate isDefault() { this.getPrefix() = "" }
|
||||
|
||||
override string toString() {
|
||||
this.isDefault() and result = this.getUri()
|
||||
this.isDefault() and result = this.getURI()
|
||||
or
|
||||
not this.isDefault() and result = this.getPrefix() + ":" + this.getUri()
|
||||
not this.isDefault() and result = this.getPrefix() + ":" + this.getURI()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ predicate dependsOnTransitive(DependsSource src, Element dest) {
|
||||
/**
|
||||
* A dependency that targets a TypeDeclarationEntry.
|
||||
*/
|
||||
private predicate dependsOnTde(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
private predicate dependsOnTDE(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
dependsOnTransitive(src, t) and
|
||||
getDeclarationEntries(t, dest)
|
||||
}
|
||||
@@ -247,8 +247,8 @@ private predicate dependsOnTde(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
* A dependency that targets a visible TypeDeclarationEntry.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate dependsOnVisibleTde(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
dependsOnTde(src, t, dest) and
|
||||
private predicate dependsOnVisibleTDE(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
dependsOnTDE(src, t, dest) and
|
||||
exists(File g | g = dest.getFile() |
|
||||
exists(File f | f = src.getFile() | f.getAnIncludedFile*() = g)
|
||||
)
|
||||
@@ -260,8 +260,8 @@ private predicate dependsOnVisibleTde(Element src, Type t, TypeDeclarationEntry
|
||||
private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest) {
|
||||
exists(Type t |
|
||||
// dependency from a Type use -> unique visible TDE
|
||||
dependsOnVisibleTde(src, t, dest) and
|
||||
strictcount(TypeDeclarationEntry alt | dependsOnVisibleTde(src, t, alt)) = 1
|
||||
dependsOnVisibleTDE(src, t, dest) and
|
||||
strictcount(TypeDeclarationEntry alt | dependsOnVisibleTDE(src, t, alt)) = 1
|
||||
)
|
||||
or
|
||||
exists(TypedefType mid |
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import semmle.code.cpp.Macro
|
||||
|
||||
/** A macro defining NULL. */
|
||||
class NullMacro extends Macro {
|
||||
NullMacro() { this.getHead() = "NULL" }
|
||||
class NULLMacro extends Macro {
|
||||
NULLMacro() { this.getHead() = "NULL" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NullMacro */
|
||||
deprecated class NULLMacro = NullMacro;
|
||||
|
||||
/** A use of the NULL macro. */
|
||||
class NULL extends Literal {
|
||||
NULL() { exists(NullMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) }
|
||||
NULL() { exists(NULLMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) }
|
||||
}
|
||||
|
||||
@@ -143,28 +143,6 @@ class ScanfFunctionCall extends FunctionCall {
|
||||
* (rather than a `char*`).
|
||||
*/
|
||||
predicate isWideCharDefault() { this.getScanfFunction().isWideCharDefault() }
|
||||
|
||||
/**
|
||||
* Gets the output argument at position `n` in the vararg list of this call.
|
||||
*
|
||||
* The range of `n` is from `0` to `this.getNumberOfOutputArguments() - 1`.
|
||||
*/
|
||||
Expr getOutputArgument(int n) {
|
||||
result = this.getArgument(this.getTarget().getNumberOfParameters() + n) and
|
||||
n >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an output argument given to this call in vararg position.
|
||||
*/
|
||||
Expr getAnOutputArgument() { result = this.getOutputArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the number of output arguments present in this call.
|
||||
*/
|
||||
int getNumberOfOutputArguments() {
|
||||
result = this.getNumberOfArguments() - this.getTarget().getNumberOfParameters()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import cpp
|
||||
* A function that concatenates the string from its second argument
|
||||
* to the string from its first argument, for example `strcat`.
|
||||
*/
|
||||
deprecated class StrcatFunction extends Function {
|
||||
class StrcatFunction extends Function {
|
||||
StrcatFunction() {
|
||||
getName() =
|
||||
[
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
* _sink_.
|
||||
*
|
||||
* Unless configured otherwise, _flow_ means that the exact value of
|
||||
* the source may reach the sink.
|
||||
* the source may reach the sink. We do not track flow across pointer
|
||||
* dereferences or array indexing. To track these types of flow, where the
|
||||
* exact value may not be preserved, import
|
||||
* `semmle.code.cpp.dataflow.TaintTracking`.
|
||||
*
|
||||
* To use global (interprocedural) data flow, extend the class
|
||||
* `DataFlow::Configuration` as documented on that class. To use local
|
||||
@@ -14,4 +17,8 @@
|
||||
* `DataFlow::Node`.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import cpp
|
||||
|
||||
module DataFlow {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl
|
||||
}
|
||||
|
||||
@@ -9,4 +9,8 @@
|
||||
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
import cpp
|
||||
|
||||
module DataFlow2 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl2
|
||||
}
|
||||
|
||||
@@ -9,4 +9,8 @@
|
||||
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
import cpp
|
||||
|
||||
module DataFlow3 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl3
|
||||
}
|
||||
|
||||
@@ -9,4 +9,8 @@
|
||||
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow4
|
||||
import cpp
|
||||
|
||||
module DataFlow4 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl4
|
||||
}
|
||||
|
||||
@@ -95,11 +95,6 @@ predicate stackPointerFlowsToUse(Expr use, Type useType, Expr source, boolean is
|
||||
cached
|
||||
private PointerType getExprPtrType(Expr use) { result = use.getUnspecifiedType() }
|
||||
|
||||
/**
|
||||
* Holds if `use` has type `useType` and `source` is an access to a stack variable
|
||||
* that flows to `use`. `isLocal` is `true` if `use` is accessed via a parameter, and
|
||||
* `false` otherwise.
|
||||
*/
|
||||
predicate stackReferenceFlowsToUse(Expr use, Type useType, Expr source, boolean isLocal) {
|
||||
// Stack variables
|
||||
exists(StackVariable var |
|
||||
|
||||
@@ -15,4 +15,9 @@
|
||||
* `TaintTracking::localTaintStep` with arguments of type `DataFlow::Node`.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
|
||||
module TaintTracking {
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
*
|
||||
* See `semmle.code.cpp.dataflow.TaintTracking` for the full documentation.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking2
|
||||
module TaintTracking2 {
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking2.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Provides a `TaintTracking3` module, which is a copy of the `TaintTracking`
|
||||
* module. Use this class when data-flow configurations or taint-tracking
|
||||
* configurations must depend on each other. Two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other, but one of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. The
|
||||
* `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and
|
||||
* `TaintTracking2::Configuration` extends `DataFlow2::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.dataflow.TaintTracking` for the full documentation.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking3
|
||||
@@ -1,6 +1,6 @@
|
||||
private import cpp
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowUtil
|
||||
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`.
|
||||
@@ -163,9 +163,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) {
|
||||
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
|
||||
}
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -560,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
|
||||
contentType) and
|
||||
hasReadStep(tc.getContent(), config) and
|
||||
read(_, tc.getContent(), _, config) and
|
||||
stepFilter(node1, node2, config)
|
||||
}
|
||||
|
||||
@@ -603,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class ApApprox = Unit;
|
||||
|
||||
class Ap = Unit;
|
||||
|
||||
private class Cc = boolean;
|
||||
class ApOption = Unit;
|
||||
|
||||
class Cc = boolean;
|
||||
|
||||
/* Begin: Stage 1 logic. */
|
||||
/**
|
||||
@@ -614,7 +613,7 @@ private module Stage1 implements StageSig {
|
||||
* The Boolean `cc` records whether the node is reached through an
|
||||
* argument in a call.
|
||||
*/
|
||||
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
sourceNode(node, _, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
or
|
||||
@@ -754,7 +753,7 @@ private module Stage1 implements StageSig {
|
||||
* the enclosing callable in order to reach a sink.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
revFlow0(node, toReturn, config) and
|
||||
fwdFlow(node, config)
|
||||
}
|
||||
@@ -838,13 +837,13 @@ private module Stage1 implements StageSig {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableReturnPosOutNodeCandFwd1(
|
||||
predicate viableReturnPosOutNodeCandFwd1(
|
||||
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
|
||||
) {
|
||||
fwdFlowReturnPosition(pos, _, config) and
|
||||
@@ -860,7 +859,7 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
@@ -907,7 +906,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlowState(FlowState state, Configuration config) {
|
||||
predicate revFlowState(FlowState state, Configuration config) {
|
||||
exists(NodeEx node |
|
||||
sinkNode(node, state, config) and
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -999,7 +998,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -1260,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
@@ -1484,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
@@ -1662,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
revFlow(node, state, _, _, _, config)
|
||||
}
|
||||
|
||||
@@ -1675,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
revFlow(node, _, _, _, _, config)
|
||||
}
|
||||
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, state, ap, config)
|
||||
}
|
||||
|
||||
@@ -1702,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
@@ -1744,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -2929,15 +2926,10 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private PathNodeImpl getANonHiddenSuccessor0() {
|
||||
result = this.getASuccessorIfHidden*() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and
|
||||
not this.isHidden()
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
@@ -163,9 +163,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) {
|
||||
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
|
||||
}
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -560,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
|
||||
contentType) and
|
||||
hasReadStep(tc.getContent(), config) and
|
||||
read(_, tc.getContent(), _, config) and
|
||||
stepFilter(node1, node2, config)
|
||||
}
|
||||
|
||||
@@ -603,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class ApApprox = Unit;
|
||||
|
||||
class Ap = Unit;
|
||||
|
||||
private class Cc = boolean;
|
||||
class ApOption = Unit;
|
||||
|
||||
class Cc = boolean;
|
||||
|
||||
/* Begin: Stage 1 logic. */
|
||||
/**
|
||||
@@ -614,7 +613,7 @@ private module Stage1 implements StageSig {
|
||||
* The Boolean `cc` records whether the node is reached through an
|
||||
* argument in a call.
|
||||
*/
|
||||
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
sourceNode(node, _, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
or
|
||||
@@ -754,7 +753,7 @@ private module Stage1 implements StageSig {
|
||||
* the enclosing callable in order to reach a sink.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
revFlow0(node, toReturn, config) and
|
||||
fwdFlow(node, config)
|
||||
}
|
||||
@@ -838,13 +837,13 @@ private module Stage1 implements StageSig {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableReturnPosOutNodeCandFwd1(
|
||||
predicate viableReturnPosOutNodeCandFwd1(
|
||||
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
|
||||
) {
|
||||
fwdFlowReturnPosition(pos, _, config) and
|
||||
@@ -860,7 +859,7 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
@@ -907,7 +906,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlowState(FlowState state, Configuration config) {
|
||||
predicate revFlowState(FlowState state, Configuration config) {
|
||||
exists(NodeEx node |
|
||||
sinkNode(node, state, config) and
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -999,7 +998,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -1260,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
@@ -1484,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
@@ -1662,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
revFlow(node, state, _, _, _, config)
|
||||
}
|
||||
|
||||
@@ -1675,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
revFlow(node, _, _, _, _, config)
|
||||
}
|
||||
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, state, ap, config)
|
||||
}
|
||||
|
||||
@@ -1702,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
@@ -1744,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -2929,15 +2926,10 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private PathNodeImpl getANonHiddenSuccessor0() {
|
||||
result = this.getASuccessorIfHidden*() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and
|
||||
not this.isHidden()
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
@@ -163,9 +163,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) {
|
||||
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
|
||||
}
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -560,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
|
||||
contentType) and
|
||||
hasReadStep(tc.getContent(), config) and
|
||||
read(_, tc.getContent(), _, config) and
|
||||
stepFilter(node1, node2, config)
|
||||
}
|
||||
|
||||
@@ -603,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class ApApprox = Unit;
|
||||
|
||||
class Ap = Unit;
|
||||
|
||||
private class Cc = boolean;
|
||||
class ApOption = Unit;
|
||||
|
||||
class Cc = boolean;
|
||||
|
||||
/* Begin: Stage 1 logic. */
|
||||
/**
|
||||
@@ -614,7 +613,7 @@ private module Stage1 implements StageSig {
|
||||
* The Boolean `cc` records whether the node is reached through an
|
||||
* argument in a call.
|
||||
*/
|
||||
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
sourceNode(node, _, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
or
|
||||
@@ -754,7 +753,7 @@ private module Stage1 implements StageSig {
|
||||
* the enclosing callable in order to reach a sink.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
revFlow0(node, toReturn, config) and
|
||||
fwdFlow(node, config)
|
||||
}
|
||||
@@ -838,13 +837,13 @@ private module Stage1 implements StageSig {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableReturnPosOutNodeCandFwd1(
|
||||
predicate viableReturnPosOutNodeCandFwd1(
|
||||
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
|
||||
) {
|
||||
fwdFlowReturnPosition(pos, _, config) and
|
||||
@@ -860,7 +859,7 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
@@ -907,7 +906,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlowState(FlowState state, Configuration config) {
|
||||
predicate revFlowState(FlowState state, Configuration config) {
|
||||
exists(NodeEx node |
|
||||
sinkNode(node, state, config) and
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -999,7 +998,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -1260,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
@@ -1484,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
@@ -1662,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
revFlow(node, state, _, _, _, config)
|
||||
}
|
||||
|
||||
@@ -1675,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
revFlow(node, _, _, _, _, config)
|
||||
}
|
||||
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, state, ap, config)
|
||||
}
|
||||
|
||||
@@ -1702,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
@@ -1744,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -2929,15 +2926,10 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private PathNodeImpl getANonHiddenSuccessor0() {
|
||||
result = this.getASuccessorIfHidden*() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and
|
||||
not this.isHidden()
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
@@ -163,9 +163,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) {
|
||||
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
|
||||
}
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -560,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
|
||||
contentType) and
|
||||
hasReadStep(tc.getContent(), config) and
|
||||
read(_, tc.getContent(), _, config) and
|
||||
stepFilter(node1, node2, config)
|
||||
}
|
||||
|
||||
@@ -603,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class ApApprox = Unit;
|
||||
|
||||
class Ap = Unit;
|
||||
|
||||
private class Cc = boolean;
|
||||
class ApOption = Unit;
|
||||
|
||||
class Cc = boolean;
|
||||
|
||||
/* Begin: Stage 1 logic. */
|
||||
/**
|
||||
@@ -614,7 +613,7 @@ private module Stage1 implements StageSig {
|
||||
* The Boolean `cc` records whether the node is reached through an
|
||||
* argument in a call.
|
||||
*/
|
||||
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
sourceNode(node, _, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
or
|
||||
@@ -754,7 +753,7 @@ private module Stage1 implements StageSig {
|
||||
* the enclosing callable in order to reach a sink.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
revFlow0(node, toReturn, config) and
|
||||
fwdFlow(node, config)
|
||||
}
|
||||
@@ -838,13 +837,13 @@ private module Stage1 implements StageSig {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableReturnPosOutNodeCandFwd1(
|
||||
predicate viableReturnPosOutNodeCandFwd1(
|
||||
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
|
||||
) {
|
||||
fwdFlowReturnPosition(pos, _, config) and
|
||||
@@ -860,7 +859,7 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
@@ -907,7 +906,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlowState(FlowState state, Configuration config) {
|
||||
predicate revFlowState(FlowState state, Configuration config) {
|
||||
exists(NodeEx node |
|
||||
sinkNode(node, state, config) and
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -999,7 +998,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -1260,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
@@ -1484,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
@@ -1662,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
revFlow(node, state, _, _, _, config)
|
||||
}
|
||||
|
||||
@@ -1675,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
revFlow(node, _, _, _, _, config)
|
||||
}
|
||||
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, state, ap, config)
|
||||
}
|
||||
|
||||
@@ -1702,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
@@ -1744,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -2929,15 +2926,10 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private PathNodeImpl getANonHiddenSuccessor0() {
|
||||
result = this.getASuccessorIfHidden*() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and
|
||||
not this.isHidden()
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
@@ -709,8 +709,7 @@ private module Cached {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
result = viableImplInCallContext(call, ctx)
|
||||
or
|
||||
result = viableCallableLambda(call, TDataFlowCallSome(ctx))
|
||||
or
|
||||
@@ -32,19 +32,6 @@ module Consistency {
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
|
||||
predicate reverseReadExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `postHasUniquePre`. */
|
||||
predicate postHasUniquePreExclude(PostUpdateNode n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniquePostUpdate`. */
|
||||
predicate uniquePostUpdateExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `(call, ctx)` should be excluded from the consistency test `viableImplInCallContextTooLargeExclude`. */
|
||||
predicate viableImplInCallContextTooLargeExclude(
|
||||
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -179,7 +166,6 @@ module Consistency {
|
||||
}
|
||||
|
||||
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
|
||||
not any(ConsistencyConfiguration conf).postHasUniquePreExclude(n) and
|
||||
exists(int c |
|
||||
c = count(n.getPreUpdateNode()) and
|
||||
c != 1 and
|
||||
@@ -188,7 +174,6 @@ module Consistency {
|
||||
}
|
||||
|
||||
query predicate uniquePostUpdate(Node n, string msg) {
|
||||
not any(ConsistencyConfiguration conf).uniquePostUpdateExclude(n) and
|
||||
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
|
||||
msg = "Node has multiple PostUpdateNodes."
|
||||
}
|
||||
@@ -224,12 +209,4 @@ module Consistency {
|
||||
not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and
|
||||
msg = "PostUpdateNode should not be the target of local flow."
|
||||
}
|
||||
|
||||
query predicate viableImplInCallContextTooLarge(
|
||||
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
|
||||
) {
|
||||
callable = viableImplInCallContext(call, ctx) and
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
}
|
||||
@@ -163,9 +163,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) {
|
||||
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
|
||||
}
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -560,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
|
||||
pragma[nomagic]
|
||||
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(
|
||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
|
||||
contentType) and
|
||||
hasReadStep(tc.getContent(), config) and
|
||||
read(_, tc.getContent(), _, config) and
|
||||
stepFilter(node1, node2, config)
|
||||
}
|
||||
|
||||
@@ -603,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class ApApprox = Unit;
|
||||
|
||||
class Ap = Unit;
|
||||
|
||||
private class Cc = boolean;
|
||||
class ApOption = Unit;
|
||||
|
||||
class Cc = boolean;
|
||||
|
||||
/* Begin: Stage 1 logic. */
|
||||
/**
|
||||
@@ -614,7 +613,7 @@ private module Stage1 implements StageSig {
|
||||
* The Boolean `cc` records whether the node is reached through an
|
||||
* argument in a call.
|
||||
*/
|
||||
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||
sourceNode(node, _, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
or
|
||||
@@ -754,7 +753,7 @@ private module Stage1 implements StageSig {
|
||||
* the enclosing callable in order to reach a sink.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
|
||||
revFlow0(node, toReturn, config) and
|
||||
fwdFlow(node, config)
|
||||
}
|
||||
@@ -838,13 +837,13 @@ private module Stage1 implements StageSig {
|
||||
* by `revFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
|
||||
revFlowConsCand(c, conf) and
|
||||
revFlowStore(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableReturnPosOutNodeCandFwd1(
|
||||
predicate viableReturnPosOutNodeCandFwd1(
|
||||
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
|
||||
) {
|
||||
fwdFlowReturnPosition(pos, _, config) and
|
||||
@@ -860,7 +859,7 @@ private module Stage1 implements StageSig {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
@@ -907,7 +906,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlowState(FlowState state, Configuration config) {
|
||||
predicate revFlowState(FlowState state, Configuration config) {
|
||||
exists(NodeEx node |
|
||||
sinkNode(node, state, config) and
|
||||
revFlow(node, _, pragma[only_bind_into](config)) and
|
||||
@@ -999,7 +998,7 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -1260,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
@@ -1484,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
* the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
@@ -1662,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
|
||||
revFlow(node, state, _, _, _, config)
|
||||
}
|
||||
|
||||
@@ -1675,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, Configuration config) {
|
||||
revFlow(node, _, _, _, _, config)
|
||||
}
|
||||
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
|
||||
|
||||
// use an alias as a workaround for bad functionality-induced joins
|
||||
pragma[nomagic]
|
||||
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
|
||||
revFlow(node, state, ap, config)
|
||||
}
|
||||
|
||||
@@ -1702,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
|
||||
revConsCand(tc, ap, config) and
|
||||
validAp(ap, config)
|
||||
}
|
||||
@@ -1744,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate stats(
|
||||
predicate stats(
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
@@ -2929,15 +2926,10 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private PathNodeImpl getANonHiddenSuccessor0() {
|
||||
result = this.getASuccessorIfHidden*() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and
|
||||
not this.isHidden()
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import FlowVar
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import AddressFlow
|
||||
private import semmle.code.cpp.dataflow.internal.AddressFlow
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.controlflow.SSA
|
||||
private import SubBasicBlocks
|
||||
private import AddressFlow
|
||||
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
|
||||
private import semmle.code.cpp.dataflow.internal.AddressFlow
|
||||
private import semmle.code.cpp.models.implementations.Iterator
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
|
||||
@@ -474,7 +474,7 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
/** Type-specialized version of `getEnclosingElement`. */
|
||||
private ControlFlowNode getCfnParent(ControlFlowNode node) { result = node.getEnclosingElement() }
|
||||
private ControlFlowNode getCFNParent(ControlFlowNode node) { result = node.getEnclosingElement() }
|
||||
|
||||
/**
|
||||
* A for-loop or while-loop whose condition is always true upon entry but not
|
||||
@@ -526,7 +526,7 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
private predicate bbInLoopCondition(BasicBlock bb) {
|
||||
getCfnParent*(bb.getANode()) = this.(Loop).getCondition()
|
||||
getCFNParent*(bb.getANode()) = this.(Loop).getCondition()
|
||||
}
|
||||
|
||||
private predicate bbInLoop(BasicBlock bb) {
|
||||
@@ -14,7 +14,7 @@ private import semmle.code.cpp.models.interfaces.Iterator
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
|
||||
private module DataFlow {
|
||||
import DataFlowUtil
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowUtil
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,12 +172,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(
|
||||
this.isSink(node) or
|
||||
this.isSink(node, _) or
|
||||
this.isAdditionalTaintStep(node, _) or
|
||||
this.isAdditionalTaintStep(node, _, _, _)
|
||||
) and
|
||||
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user