mirror of
https://github.com/github/codeql.git
synced 2026-05-26 09:01:22 +02:00
Compare commits
14 Commits
tiferet/ad
...
tiferet/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3af4e65695 | ||
|
|
a67886e0aa | ||
|
|
f8336ce9be | ||
|
|
68da966732 | ||
|
|
61360577ba | ||
|
|
099916f88f | ||
|
|
fbcfd523f4 | ||
|
|
7a8715d1ef | ||
|
|
c92bc77b59 | ||
|
|
3f1ca89bd3 | ||
|
|
2a324f5c5d | ||
|
|
9a8b0d7fb2 | ||
|
|
dfbfa5d27d | ||
|
|
4a2046476a |
@@ -23,19 +23,20 @@ runs:
|
||||
run: |
|
||||
MERGE_BASE=$(git cat-file commit $GITHUB_SHA | grep '^parent ' | head -1 | cut -f 2 -d " ")
|
||||
echo "merge_base=$MERGE_BASE" >> $GITHUB_ENV
|
||||
- name: Restore cache (PR)
|
||||
- name: Restore read-only cache (PR)
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: actions/cache/restore@v3
|
||||
uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
|
||||
with:
|
||||
path: '**/.cache'
|
||||
read-only: true
|
||||
key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }}
|
||||
restore-keys: |
|
||||
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-${{ env.merge_base }}
|
||||
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-
|
||||
codeql-compile-${{ inputs.key }}-main-
|
||||
- name: Fill cache (only branch push)
|
||||
- name: Fill cache (push)
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: actions/cache@v3
|
||||
uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
|
||||
with:
|
||||
path: '**/.cache'
|
||||
key: codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-${{ github.sha }} # just fill on main
|
||||
|
||||
32
.github/actions/os-version/action.yml
vendored
32
.github/actions/os-version/action.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: OS Version
|
||||
description: Get OS version.
|
||||
|
||||
outputs:
|
||||
version:
|
||||
description: "OS version"
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
. /etc/os-release
|
||||
echo "VERSION=${NAME} ${VERSION}" >> $GITHUB_ENV
|
||||
- if: runner.os == 'Windows'
|
||||
shell: powershell
|
||||
run: |
|
||||
$objects = systeminfo.exe /FO CSV | ConvertFrom-Csv
|
||||
"VERSION=$($objects.'OS Name') $($objects.'OS Version')" >> $env:GITHUB_ENV
|
||||
- if: runner.os == 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "VERSION=$(sw_vers -productName) $(sw_vers -productVersion)" >> $GITHUB_ENV
|
||||
- name: Emit OS version
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
echo "$VERSION"
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
11
.github/workflows/atm-check-query-suite.yml
vendored
11
.github/workflows/atm-check-query-suite.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
jobs:
|
||||
atm-check-query-suite:
|
||||
runs-on: ubuntu-latest-xl
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -23,12 +23,6 @@ jobs:
|
||||
with:
|
||||
channel: release
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: atm-suite
|
||||
|
||||
- name: Install ATM model
|
||||
run: |
|
||||
set -exu
|
||||
@@ -56,13 +50,10 @@ jobs:
|
||||
echo "SARIF_PATH=${SARIF_PATH}" >> "${GITHUB_ENV}"
|
||||
|
||||
codeql database analyze \
|
||||
--threads=0 \
|
||||
--ram 50000 \
|
||||
--format sarif-latest \
|
||||
--output "${SARIF_PATH}" \
|
||||
--sarif-group-rules-by-pack \
|
||||
-vv \
|
||||
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
|
||||
-- \
|
||||
"${DB_PATH}" \
|
||||
"${QUERY_PACK}/${QUERY_SUITE}"
|
||||
|
||||
6
.github/workflows/check-change-note.yml
vendored
6
.github/workflows/check-change-note.yml
vendored
@@ -26,9 +26,3 @@ jobs:
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
|
||||
grep true -c
|
||||
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md' or 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text.
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$"))' |
|
||||
grep true -c
|
||||
|
||||
21
.github/workflows/check-query-ids.yml
vendored
21
.github/workflows/check-query-ids.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: Check query IDs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**/src/**/*.ql"
|
||||
- misc/scripts/check-query-ids.py
|
||||
- .github/workflows/check-query-ids.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check query IDs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check for duplicate query IDs
|
||||
run: python3 misc/scripts/check-query-ids.py
|
||||
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@v7
|
||||
- uses: actions/stale@v6
|
||||
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.'
|
||||
|
||||
10
.github/workflows/mad_modelDiff.yml
vendored
10
.github/workflows/mad_modelDiff.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "java/ql/src/utils/modelgenerator/**/*.*"
|
||||
- "java/ql/src/utils/model-generator/**/*.*"
|
||||
- ".github/workflows/mad_modelDiff.yml"
|
||||
|
||||
permissions:
|
||||
@@ -40,12 +40,12 @@ jobs:
|
||||
- name: Download database
|
||||
env:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -x
|
||||
mkdir lib-dbs
|
||||
SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}
|
||||
gh api -H "Accept: application/zip" "/repos/${SLUG}/code-scanning/codeql/databases/java" > "$SHORTNAME.zip"
|
||||
projectId=`curl -s https://lgtm.com/api/v1.0/projects/g/${SLUG} | jq .id`
|
||||
curl -L "https://lgtm.com/api/v1.0/snapshots/$projectId/java" -o "$SHORTNAME.zip"
|
||||
unzip -q -d "${SHORTNAME}-db" "${SHORTNAME}.zip"
|
||||
mkdir "lib-dbs/$SHORTNAME/"
|
||||
mv "${SHORTNAME}-db/"$(ls -1 "${SHORTNAME}"-db)/* "lib-dbs/${SHORTNAME}/"
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
DATABASE=$2
|
||||
cd codeql-$QL_VARIANT
|
||||
SHORTNAME=`basename $DATABASE`
|
||||
python java/ql/src/utils/modelgenerator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE ${SHORTNAME}.temp.model.yml
|
||||
python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE ${SHORTNAME}.temp.model.yml
|
||||
mv java/ql/lib/ext/generated/${SHORTNAME}.temp.model.yml $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.model.yml
|
||||
cd ..
|
||||
}
|
||||
@@ -100,6 +100,4 @@ jobs:
|
||||
with:
|
||||
name: diffs
|
||||
path: tmp-models/*.html
|
||||
# An html file is only produced if the generated models differ.
|
||||
if-no-files-found: ignore
|
||||
retention-days: 20
|
||||
|
||||
2
.github/workflows/mad_regenerate-models.yml
vendored
2
.github/workflows/mad_regenerate-models.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
run: |
|
||||
SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}
|
||||
java/ql/src/utils/modelgenerator/RegenerateModels.py "${SLUG}" dbs/${SHORTNAME}
|
||||
java/ql/src/utils/model-generator/RegenerateModels.py "${SLUG}" dbs/${SHORTNAME}
|
||||
- name: Stage changes
|
||||
run: |
|
||||
find java -name "*.model.yml" -print0 | xargs -0 git add
|
||||
|
||||
20
.github/workflows/ql-for-ql-build.yml
vendored
20
.github/workflows/ql-for-ql-build.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
uses: ./.github/actions/find-latest-bundle
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
@@ -38,14 +38,12 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- name: Cache entire pack
|
||||
id: cache-pack
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ runner.temp }}/pack
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-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') }}
|
||||
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
|
||||
@@ -79,7 +77,7 @@ jobs:
|
||||
ql/target/release/ql-autobuilder.exe
|
||||
ql/target/release/ql-extractor
|
||||
ql/target/release/ql-extractor.exe
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
|
||||
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'
|
||||
uses: actions/cache@v3
|
||||
@@ -88,7 +86,7 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
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'
|
||||
run: cd ql; cargo fmt --all -- --check
|
||||
@@ -139,20 +137,20 @@ jobs:
|
||||
env:
|
||||
CONF: ./ql-for-ql-config.yml
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: ql
|
||||
db-location: ${{ runner.temp }}/db
|
||||
config-file: ./ql-for-ql-config.yml
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
- name: Move pack queries
|
||||
- name: Move pack cache
|
||||
run: |
|
||||
cp -r ${PACK}/queries ql/ql/src
|
||||
cp -r ${PACK}/.cache ql/ql/src/.cache
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/analyze@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
category: "ql-for-ql"
|
||||
- name: Copy sarif file to CWD
|
||||
@@ -174,4 +172,4 @@ jobs:
|
||||
with:
|
||||
name: ql-for-ql-langs
|
||||
path: split-sarif
|
||||
retention-days: 1
|
||||
retention-days: 1
|
||||
@@ -25,18 +25,16 @@ jobs:
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Build Extractor
|
||||
run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./scripts/create-extractor-pack.sh
|
||||
env:
|
||||
|
||||
70
.github/workflows/ql-for-ql-tests.yml
vendored
70
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -20,86 +20,30 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: ql-for-ql-tests
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" ql/ql/test
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
|
||||
other-os:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest]
|
||||
needs: [qltest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install GNU tar
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install gnu-tar
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
|
||||
- name: Build extractor (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
cd ql;
|
||||
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
|
||||
pwsh ./scripts/create-extractor-pack.ps1
|
||||
- name: Run a single QL tests - Unix
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries ql/ql/test
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Run a single QL tests - Windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
- name: Check QL formatting
|
||||
run: |
|
||||
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
|
||||
codeql test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
|
||||
|
||||
find ql/ql/src "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
|
||||
31
.github/workflows/ruby-build.yml
vendored
31
.github/workflows/ruby-build.yml
vendored
@@ -48,8 +48,6 @@ jobs:
|
||||
run: |
|
||||
brew install gnu-tar
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- name: Cache entire extractor
|
||||
uses: actions/cache@v3
|
||||
id: cache-extractor
|
||||
@@ -60,7 +58,7 @@ jobs:
|
||||
ruby/target/release/ruby-extractor
|
||||
ruby/target/release/ruby-extractor.exe
|
||||
ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}--${{ hashFiles('ruby/**/*.rs') }}
|
||||
key: ${{ runner.os }}-ruby-extractor-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}--${{ hashFiles('ruby/**/*.rs') }}
|
||||
- uses: actions/cache@v3
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
with:
|
||||
@@ -68,7 +66,7 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ruby/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo fmt --all -- --check
|
||||
@@ -117,10 +115,9 @@ jobs:
|
||||
- name: Build Query Pack
|
||||
run: |
|
||||
rm -rf target/packs
|
||||
codeql pack create ../shared/ssa --output target/packs
|
||||
codeql pack create ../misc/suite-helpers --output target/packs
|
||||
codeql pack create ../shared/regex --output target/packs
|
||||
codeql pack create ../shared/ssa --output target/packs
|
||||
codeql pack create ../shared/tutorial --output target/packs
|
||||
codeql pack create ql/lib --output target/packs
|
||||
codeql pack create -j0 ql/src --output target/packs --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*)
|
||||
@@ -205,6 +202,11 @@ jobs:
|
||||
- name: Fetch CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Shopify/example-ruby-app
|
||||
ref: 67a0decc5eb550f3a9228eda53925c3afd40dfe9
|
||||
|
||||
- name: Download Ruby bundle
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -213,15 +215,26 @@ jobs:
|
||||
- name: Unzip Ruby bundle
|
||||
shell: bash
|
||||
run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip"
|
||||
|
||||
- name: Prepare test files
|
||||
shell: bash
|
||||
run: |
|
||||
echo "import codeql.ruby.AST select count(File f)" > "test.ql"
|
||||
echo "| 4 |" > "test.expected"
|
||||
echo 'name: sample-tests
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
codeql/ruby-all: "*"
|
||||
extractor: ruby
|
||||
tests: .
|
||||
' > qlpack.yml
|
||||
- name: Run QL test
|
||||
shell: bash
|
||||
run: |
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" ruby/ql/test/library-tests/ast/constants/
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" .
|
||||
- name: Create database
|
||||
shell: bash
|
||||
run: |
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root ruby/ql/test/library-tests/ast/constants/ ../database
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database
|
||||
- name: Analyze database
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
1
.github/workflows/swift.yml
vendored
1
.github/workflows/swift.yml
vendored
@@ -65,7 +65,6 @@ jobs:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
needs: build-and-test-macos
|
||||
runs-on: macos-12-xl
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-integration-tests
|
||||
|
||||
@@ -25,7 +25,6 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
|
||||
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
||||
- Experimental queries need to include `experimental` in their `@tags`
|
||||
- The structure of an `experimental` subdirectory mirrors the structure of its parent directory.
|
||||
- Select or create an appropriate directory in `experimental` based on the existing directory structure of `experimental` or its parent directory.
|
||||
|
||||
|
||||
@@ -402,6 +402,16 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
],
|
||||
"Inline Test Expectations": [
|
||||
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"csharp/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"java/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"python/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"ruby/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"ql/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"go/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"swift/ql/test/TestUtilities/InlineExpectationsTest.qll"
|
||||
],
|
||||
"C++ ExternalAPIs": [
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"
|
||||
@@ -460,10 +470,6 @@
|
||||
"javascript/ql/src/Comments/CommentedOutCodeReferences.inc.qhelp",
|
||||
"python/ql/src/Lexical/CommentedOutCodeReferences.inc.qhelp"
|
||||
],
|
||||
"ThreadResourceAbuse qhelp": [
|
||||
"java/ql/src/experimental/Security/CWE/CWE-400/LocalThreadResourceAbuse.qhelp",
|
||||
"java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qhelp"
|
||||
],
|
||||
"IDE Contextual Queries": [
|
||||
"cpp/ql/lib/IDEContextual.qll",
|
||||
"csharp/ql/lib/IDEContextual.qll",
|
||||
@@ -495,6 +501,14 @@
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
|
||||
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
|
||||
],
|
||||
"CodeQL Tutorial": [
|
||||
"cpp/ql/lib/tutorial.qll",
|
||||
"csharp/ql/lib/tutorial.qll",
|
||||
"java/ql/lib/tutorial.qll",
|
||||
"javascript/ql/lib/tutorial.qll",
|
||||
"python/ql/lib/tutorial.qll",
|
||||
"ruby/ql/lib/tutorial.qll"
|
||||
],
|
||||
"AccessPathSyntax": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
|
||||
@@ -513,16 +527,16 @@
|
||||
"ruby/ql/lib/codeql/ruby/internal/ConceptsShared.qll",
|
||||
"javascript/ql/lib/semmle/javascript/internal/ConceptsShared.qll"
|
||||
],
|
||||
"Hostname Regexp queries": [
|
||||
"javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll",
|
||||
"python/ql/src/Security/CWE-020/HostnameRegexpShared.qll",
|
||||
"ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll"
|
||||
],
|
||||
"ApiGraphModels": [
|
||||
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll",
|
||||
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll"
|
||||
],
|
||||
"ApiGraphModelsExtensions": [
|
||||
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExtensions.qll",
|
||||
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll"
|
||||
],
|
||||
"TaintedFormatStringQuery Ruby/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringQuery.qll"
|
||||
@@ -566,9 +580,5 @@
|
||||
"IncompleteMultiCharacterSanitization JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
|
||||
],
|
||||
"EncryptionKeySizes Python/Java": [
|
||||
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
|
||||
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ predicate isExprWithNewBuiltin(Expr expr) {
|
||||
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
|
||||
if isExprWithNewBuiltin(expr) then kind_new = 0 else kind_new = kind
|
||||
select expr, kind_new, location
|
||||
|
||||
@@ -9,5 +9,5 @@ class Location extends @location_expr {
|
||||
from Expr expr, int kind, int kind_new, Location location
|
||||
where
|
||||
exprs(expr, kind, location) and
|
||||
if expr instanceof @blockassignexpr then kind_new = 1 else kind_new = kind
|
||||
if expr instanceof @blockassignexpr then kind_new = 0 else kind_new = kind
|
||||
select expr, kind_new, location
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
class BuiltinType extends @builtintype {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from BuiltinType type, string name, int kind, int kind_new, int size, int sign, int alignment
|
||||
where
|
||||
builtintypes(type, name, kind, size, sign, alignment) and
|
||||
if type instanceof @float16 or type instanceof @complex_float16
|
||||
then kind_new = 2
|
||||
else kind_new = kind
|
||||
select type, name, kind_new, size, sign, alignment
|
||||
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: Introduce (_Complex) _Float16 type
|
||||
compatibility: backwards
|
||||
builtintypes.rel: run builtintypes.qlo
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Uncomment case splits in dbscheme
|
||||
compatibility: full
|
||||
@@ -1,32 +1,3 @@
|
||||
## 0.5.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
The predicates in the `MustFlow::Configuration` class used by the `MustFlow` library (`semmle.code.cpp.ir.dataflow.MustFlow`) have changed to be defined directly in terms of the C++ IR instead of IR dataflow nodes.
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Deprecated `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
* Deprecated `semmle.code.cpp.security.TaintTrackingImpl`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
* Deprecated `semmle.code.cpp.valuenumbering.GlobalValueNumberingImpl`. Use `semmle.code.cpp.valuenumbering.GlobalValueNumbering`, which exposes the same API.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `ArgvSource` flow source now uses the second parameter of `main` as its source instead of the uses of this parameter.
|
||||
* The `ArgvSource` flow source has been generalized to handle cases where the argument vector of `main` is not named `argv`.
|
||||
* The `getaddrinfo` function is now recognized as a flow source.
|
||||
* The `secure_getenv` and `_wgetenv` functions are now recognized as local flow sources.
|
||||
* The `scanf` and `fscanf` functions and their variants are now recognized as flow sources.
|
||||
* Deleted the deprecated `getName` and `getShortName` predicates from the `Folder` class.
|
||||
|
||||
## 0.4.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
6
cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md
Normal file
6
cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
|
||||
|
||||
* Deprecated `semmle.code.cpp.valuenumbering.GlobalValueNumberingImpl`. Use `semmle.code.cpp.valuenumbering.GlobalValueNumbering`, which exposes the same API.
|
||||
4
cpp/ql/lib/change-notes/2022-11-16-must-flow.md
Normal file
4
cpp/ql/lib/change-notes/2022-11-16-must-flow.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
The predicates in the `MustFlow::Configuration` class used by the `MustFlow` library (`semmle.code.cpp.ir.dataflow.MustFlow`) have changed to be defined directly in terms of the C++ IR instead of IR dataflow nodes.
|
||||
4
cpp/ql/lib/change-notes/2022-11-17-deleted-deps.md
Normal file
4
cpp/ql/lib/change-notes/2022-11-17-deleted-deps.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted the deprecated `getName` and `getShortName` predicates from the `Folder` class.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.4.6
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,20 +0,0 @@
|
||||
## 0.5.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
The predicates in the `MustFlow::Configuration` class used by the `MustFlow` library (`semmle.code.cpp.ir.dataflow.MustFlow`) have changed to be defined directly in terms of the C++ IR instead of IR dataflow nodes.
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Deprecated `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
* Deprecated `semmle.code.cpp.security.TaintTrackingImpl`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
* Deprecated `semmle.code.cpp.valuenumbering.GlobalValueNumberingImpl`. Use `semmle.code.cpp.valuenumbering.GlobalValueNumbering`, which exposes the same API.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `ArgvSource` flow source now uses the second parameter of `main` as its source instead of the uses of this parameter.
|
||||
* The `ArgvSource` flow source has been generalized to handle cases where the argument vector of `main` is not named `argv`.
|
||||
* The `getaddrinfo` function is now recognized as a flow source.
|
||||
* The `secure_getenv` and `_wgetenv` functions are now recognized as local flow sources.
|
||||
* The `scanf` and `fscanf` functions and their variants are now recognized as flow sources.
|
||||
* Deleted the deprecated `getName` and `getShortName` predicates from the `Folder` class.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.0
|
||||
lastReleaseVersion: 0.4.4
|
||||
|
||||
@@ -123,13 +123,6 @@ private predicate constructorCallTypeMention(ConstructorCall cc, TypeMention tm)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `loc` has the container `container` and is on the line starting at `startLine`. */
|
||||
pragma[nomagic]
|
||||
private predicate hasContainerAndStartLine(Location loc, Container container, int startLine) {
|
||||
loc.getStartLine() = startLine and
|
||||
loc.getContainer() = container
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an element, of kind `kind`, that element `e` uses, if any.
|
||||
* Attention: This predicate yields multiple definitions for a single location.
|
||||
@@ -166,9 +159,9 @@ Top definitionOf(Top e, string kind) {
|
||||
// Multiple type mentions can be generated when a typedef is used, and
|
||||
// in such cases we want to exclude all but the originating typedef.
|
||||
not exists(Type secondary |
|
||||
exists(File f, int startline, int startcol |
|
||||
exists(TypeMention tm, File f, int startline, int startcol |
|
||||
typeMentionStartLoc(e, result, f, startline, startcol) and
|
||||
typeMentionStartLoc(_, secondary, f, startline, startcol) and
|
||||
typeMentionStartLoc(tm, secondary, f, startline, startcol) and
|
||||
(
|
||||
result = secondary.(TypedefType).getBaseType() or
|
||||
result = secondary.(TypedefType).getBaseType().(SpecifiedType).getBaseType()
|
||||
@@ -191,9 +184,11 @@ Top definitionOf(Top e, string kind) {
|
||||
kind = "I" and
|
||||
result = e.(Include).getIncludedFile() and
|
||||
// exclude `#include` directives containing macros
|
||||
not exists(MacroInvocation mi, Container container, int startLine |
|
||||
hasContainerAndStartLine(e.(Include).getLocation(), container, startLine) and
|
||||
hasContainerAndStartLine(mi.getLocation(), container, startLine)
|
||||
not exists(MacroInvocation mi, Location l1, Location l2 |
|
||||
l1 = e.(Include).getLocation() and
|
||||
l2 = mi.getLocation() and
|
||||
l1.getContainer() = l2.getContainer() and
|
||||
l1.getStartLine() = l2.getStartLine()
|
||||
// (an #include directive must be always on it's own line)
|
||||
)
|
||||
) and
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -916,56 +916,28 @@ private module Cached {
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParamNodeOption =
|
||||
TParamNodeNone() or
|
||||
TParamNodeSome(ParamNode p)
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
TAccessPathFrontNone() or
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFrontOption =
|
||||
TApproxAccessPathFrontNone() or
|
||||
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1343,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParamNode`. */
|
||||
class ParamNodeOption extends TParamNodeOption {
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParamNodeNone() and
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParamNode p |
|
||||
this = TParamNodeSome(p) and
|
||||
result = p.toString()
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1363,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption {
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
@@ -1374,82 +1346,13 @@ class ReturnCtx extends TReturnCtx {
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
this = TReturnCtxMaybeFlowThrough(pos) and
|
||||
result = pos.toString()
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional approximated access path front. */
|
||||
class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
string toString() {
|
||||
this = TApproxAccessPathFrontNone() and result = "<none>"
|
||||
or
|
||||
this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
@@ -1482,7 +1385,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract ApproxAccessPathFront toApprox();
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
@@ -1496,7 +1399,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
@@ -1508,7 +1411,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -101,7 +101,9 @@ module Consistency {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
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
|
||||
@@ -258,9 +260,4 @@ module Consistency {
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
|
||||
query predicate uniqueContentApprox(Content c, string msg) {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,13 +551,6 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox = Unit;
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
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
|
||||
|
||||
@@ -218,7 +218,7 @@ private predicate allocation(Instruction array, Length length, int delta) {
|
||||
length.(VNLength).getInstruction().getConvertedResultExpression() = lengthExpr
|
||||
)
|
||||
or
|
||||
not deconstructMallocSizeExpr(alloc.getSizeExpr(), _, _) and
|
||||
not exists(int d | deconstructMallocSizeExpr(alloc.getSizeExpr(), _, d)) and
|
||||
length.(VNLength).getInstruction().getConvertedResultExpression() = alloc.getSizeExpr() and
|
||||
delta = 0
|
||||
)
|
||||
|
||||
@@ -543,7 +543,9 @@ private predicate boundedPhiCand(
|
||||
PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta,
|
||||
Reason reason
|
||||
) {
|
||||
boundedPhiInp(phi, _, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
exists(PhiInputOperand op |
|
||||
boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -292,8 +292,12 @@ module SemanticExprConfig {
|
||||
final Location getLocation() { result = super.getLocation() }
|
||||
}
|
||||
|
||||
private class ValueNumberBound extends Bound instanceof IRBound::ValueNumberBound {
|
||||
override string toString() { result = IRBound::ValueNumberBound.super.toString() }
|
||||
private class ValueNumberBound extends Bound {
|
||||
IRBound::ValueNumberBound bound;
|
||||
|
||||
ValueNumberBound() { bound = this }
|
||||
|
||||
override string toString() { result = bound.toString() }
|
||||
}
|
||||
|
||||
predicate zeroBound(Bound bound) { bound instanceof IRBound::ZeroBound }
|
||||
|
||||
@@ -664,7 +664,9 @@ private predicate boundedPhiCand(
|
||||
SemSsaPhiNode phi, boolean upper, SemBound b, int delta, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
boundedPhiInp(phi, _, _, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
boundedPhiInp(phi, inp, edge, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,15 +33,23 @@ abstract private class FlowSignDef extends SignDef {
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
private class ExplicitSignDef extends FlowSignDef {
|
||||
SemSsaExplicitUpdate update;
|
||||
|
||||
ExplicitSignDef() { update = this }
|
||||
|
||||
final override Sign getSign() { result = semExprSign(update.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
private class PhiSignDef extends FlowSignDef {
|
||||
SemSsaPhiNode phi;
|
||||
|
||||
PhiSignDef() { phi = this }
|
||||
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
edge.phiInput(phi, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.5.1-dev
|
||||
version: 0.4.5-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
@@ -7,4 +7,3 @@ library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
|
||||
@@ -318,7 +318,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
MetricFunction getMetrics() { result = this }
|
||||
|
||||
/** Holds if this function calls the function `f`. */
|
||||
predicate calls(Function f) { this.calls(f, _) }
|
||||
predicate calls(Function f) { exists(Locatable l | this.calls(f, l)) }
|
||||
|
||||
/**
|
||||
* Holds if this function calls the function `f` in the `FunctionCall`
|
||||
@@ -335,7 +335,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
}
|
||||
|
||||
/** Holds if this function accesses a function or variable or enumerator `a`. */
|
||||
predicate accesses(Declaration a) { this.accesses(a, _) }
|
||||
predicate accesses(Declaration a) { exists(Locatable l | this.accesses(a, l)) }
|
||||
|
||||
/**
|
||||
* Holds if this function accesses a function or variable or enumerator `a`
|
||||
|
||||
@@ -10,14 +10,12 @@ import semmle.code.cpp.File
|
||||
*/
|
||||
class Location extends @location {
|
||||
/** Gets the container corresponding to this location. */
|
||||
pragma[nomagic]
|
||||
Container getContainer() { this.fullLocationInfo(result, _, _, _, _) }
|
||||
|
||||
/** Gets the file corresponding to this location, if any. */
|
||||
File getFile() { result = this.getContainer() }
|
||||
|
||||
/** Gets the 1-based line number (inclusive) where this location starts. */
|
||||
pragma[nomagic]
|
||||
int getStartLine() { this.fullLocationInfo(_, result, _, _, _) }
|
||||
|
||||
/** Gets the 1-based column number (inclusive) where this location starts. */
|
||||
@@ -65,6 +63,7 @@ class Location extends @location {
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
|
||||
@@ -816,12 +816,6 @@ private predicate floatingPointTypeMapping(
|
||||
or
|
||||
// _Float128x
|
||||
kind = 50 and base = 2 and domain = TRealDomain() and realKind = 50 and extended = true
|
||||
or
|
||||
// _Float16
|
||||
kind = 52 and base = 2 and domain = TRealDomain() and realKind = 52 and extended = false
|
||||
or
|
||||
// _Complex _Float16
|
||||
kind = 53 and base = 2 and domain = TComplexDomain() and realKind = 52 and extended = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ DependencyOptions getDependencyOptions() { any() }
|
||||
class DependsSource extends Element {
|
||||
DependsSource() {
|
||||
// not inside a template instantiation
|
||||
not this.isFromTemplateInstantiation(_) or
|
||||
not exists(Element other | this.isFromTemplateInstantiation(other)) or
|
||||
// allow DeclarationEntrys of template specializations
|
||||
this.(DeclarationEntry).getDeclaration().(Function).isConstructedFrom(_) or
|
||||
this.(DeclarationEntry).getDeclaration().(Class).isConstructedFrom(_)
|
||||
|
||||
@@ -69,9 +69,12 @@ predicate functionContainsDisabledCode(Function f) {
|
||||
*/
|
||||
predicate functionContainsPreprocCode(Function f) {
|
||||
// `f` contains a preprocessor branch
|
||||
exists(string file, int pbdStartLine, int fBlockStartLine, int fBlockEndLine |
|
||||
exists(
|
||||
PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine,
|
||||
int fBlockEndLine
|
||||
|
|
||||
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
|
||||
pbdLocation(_, file, pbdStartLine) and
|
||||
pbdLocation(pbd, file, pbdStartLine) and
|
||||
pbdStartLine <= fBlockEndLine and
|
||||
pbdStartLine >= fBlockStartLine
|
||||
)
|
||||
|
||||
@@ -397,8 +397,11 @@ private int lengthInBase16(float f) {
|
||||
/**
|
||||
* A class to represent format strings that occur as arguments to invocations of formatting functions.
|
||||
*/
|
||||
class FormatLiteral extends Literal instanceof StringLiteral {
|
||||
FormatLiteral() { exists(FormattingFunctionCall ffc | ffc.getFormat() = this) }
|
||||
class FormatLiteral extends Literal {
|
||||
FormatLiteral() {
|
||||
exists(FormattingFunctionCall ffc | ffc.getFormat() = this) and
|
||||
this instanceof StringLiteral
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function call where this format string is used.
|
||||
|
||||
@@ -30,12 +30,15 @@ abstract class ScanfFunction extends Function {
|
||||
/**
|
||||
* The standard function `scanf` (and variations).
|
||||
*/
|
||||
class Scanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
class Scanf extends ScanfFunction {
|
||||
Scanf() {
|
||||
this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
|
||||
this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
|
||||
this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
|
||||
this.hasGlobalName("_wscanf_l")
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
|
||||
this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
|
||||
this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
|
||||
this.hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
override int getInputParameterIndex() { none() }
|
||||
@@ -46,12 +49,15 @@ class Scanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
/**
|
||||
* The standard function `fscanf` (and variations).
|
||||
*/
|
||||
class Fscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
class Fscanf extends ScanfFunction {
|
||||
Fscanf() {
|
||||
this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
|
||||
this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
|
||||
this.hasGlobalName("_fwscanf_l")
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
|
||||
this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
|
||||
this.hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
override int getInputParameterIndex() { result = 0 }
|
||||
@@ -62,12 +68,15 @@ class Fscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
/**
|
||||
* The standard function `sscanf` (and variations).
|
||||
*/
|
||||
class Sscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
class Sscanf extends ScanfFunction {
|
||||
Sscanf() {
|
||||
this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
|
||||
this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
|
||||
this.hasGlobalName("_swscanf_l")
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
|
||||
this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
|
||||
this.hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
override int getInputParameterIndex() { result = 0 }
|
||||
@@ -78,12 +87,17 @@ class Sscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
/**
|
||||
* The standard(ish) function `snscanf` (and variations).
|
||||
*/
|
||||
class Snscanf extends ScanfFunction instanceof TopLevelFunction {
|
||||
class Snscanf extends ScanfFunction {
|
||||
Snscanf() {
|
||||
this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
|
||||
this.hasGlobalName("_snwscanf_l")
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
|
||||
this.hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
|
||||
// note that the max_amount is not a limit on the output length, it's an input length
|
||||
// limit used with non null-terminated strings.
|
||||
)
|
||||
}
|
||||
|
||||
override int getInputParameterIndex() { result = 0 }
|
||||
@@ -244,7 +258,9 @@ class ScanfFormatLiteral extends Expr {
|
||||
/**
|
||||
* Gets the maximum width option of the nth input (empty string if none is given).
|
||||
*/
|
||||
string getMaxWidthOpt(int n) { this.parseConvSpec(n, _, result, _, _) }
|
||||
string getMaxWidthOpt(int n) {
|
||||
exists(string spec, string len, string conv | this.parseConvSpec(n, spec, result, len, conv))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum width of the nth input.
|
||||
@@ -254,12 +270,18 @@ class ScanfFormatLiteral extends Expr {
|
||||
/**
|
||||
* Gets the length flag of the nth conversion specifier.
|
||||
*/
|
||||
string getLength(int n) { this.parseConvSpec(n, _, _, result, _) }
|
||||
string getLength(int n) {
|
||||
exists(string spec, string width, string conv |
|
||||
this.parseConvSpec(n, spec, width, result, conv)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conversion character of the nth conversion specifier.
|
||||
*/
|
||||
string getConversionChar(int n) { this.parseConvSpec(n, _, _, _, result) }
|
||||
string getConversionChar(int n) {
|
||||
exists(string spec, string width, string len | this.parseConvSpec(n, spec, width, len, result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by the nth
|
||||
|
||||
@@ -54,7 +54,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
* only condition under which a `SubBasicBlock` may have multiple
|
||||
* predecessors.
|
||||
*/
|
||||
predicate firstInBB() { this.getRankInBasicBlock(_) = 1 }
|
||||
predicate firstInBB() { exists(BasicBlock bb | this.getRankInBasicBlock(bb) = 1) }
|
||||
|
||||
/**
|
||||
* Holds if this `SubBasicBlock` comes last in its basic block. This is the
|
||||
|
||||
@@ -441,8 +441,8 @@ library class ExprEvaluator extends int {
|
||||
req = mid.(AssignExpr).getRValue()
|
||||
)
|
||||
or
|
||||
exists(Variable v, boolean sub1 |
|
||||
this.interestingVariableAccess(e, _, v, sub1) and
|
||||
exists(VariableAccess va, Variable v, boolean sub1 |
|
||||
this.interestingVariableAccess(e, va, v, sub1) and
|
||||
req = v.getAnAssignedValue() and
|
||||
(sub1 = true implies not this.ignoreVariableAssignment(e, v, req)) and
|
||||
sub = false
|
||||
@@ -876,7 +876,7 @@ private predicate nonAnalyzableVariableDefinition(Variable v, StmtParent def) {
|
||||
* empirically to have effect only on a few rare and pathological examples.
|
||||
*/
|
||||
private predicate tractableVariable(Variable v) {
|
||||
not nonAnalyzableVariableDefinition(v, _) or
|
||||
not exists(StmtParent def | nonAnalyzableVariableDefinition(v, def)) or
|
||||
strictcount(StmtParent def | nonAnalyzableVariableDefinition(v, def)) < 1000
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -916,56 +916,28 @@ private module Cached {
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParamNodeOption =
|
||||
TParamNodeNone() or
|
||||
TParamNodeSome(ParamNode p)
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
TAccessPathFrontNone() or
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFrontOption =
|
||||
TApproxAccessPathFrontNone() or
|
||||
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1343,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParamNode`. */
|
||||
class ParamNodeOption extends TParamNodeOption {
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParamNodeNone() and
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParamNode p |
|
||||
this = TParamNodeSome(p) and
|
||||
result = p.toString()
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1363,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption {
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
@@ -1374,82 +1346,13 @@ class ReturnCtx extends TReturnCtx {
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
this = TReturnCtxMaybeFlowThrough(pos) and
|
||||
result = pos.toString()
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional approximated access path front. */
|
||||
class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
string toString() {
|
||||
this = TApproxAccessPathFrontNone() and result = "<none>"
|
||||
or
|
||||
this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
@@ -1482,7 +1385,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract ApproxAccessPathFront toApprox();
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
@@ -1496,7 +1399,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
@@ -1508,7 +1411,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -101,7 +101,9 @@ module Consistency {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
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
|
||||
@@ -258,9 +260,4 @@ module Consistency {
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
|
||||
query predicate uniqueContentApprox(Content c, string msg) {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -296,13 +296,6 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox = Unit;
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
|
||||
override predicate argHasPostUpdateExclude(ArgumentNode n) {
|
||||
// Is the null pointer (or something that's not really a pointer)
|
||||
|
||||
@@ -450,8 +450,10 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
this.definedByExpr(_, _) and
|
||||
result = "assignment to " + v
|
||||
exists(Expr e |
|
||||
this.definedByExpr(e, _) and
|
||||
result = "assignment to " + v
|
||||
)
|
||||
or
|
||||
this.definedByInitialValue(_) and
|
||||
result = "initial value of " + v
|
||||
|
||||
@@ -54,7 +54,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
* only condition under which a `SubBasicBlock` may have multiple
|
||||
* predecessors.
|
||||
*/
|
||||
predicate firstInBB() { this.getRankInBasicBlock(_) = 1 }
|
||||
predicate firstInBB() { exists(BasicBlock bb | this.getRankInBasicBlock(bb) = 1) }
|
||||
|
||||
/**
|
||||
* Holds if this `SubBasicBlock` comes last in its basic block. This is the
|
||||
|
||||
@@ -70,8 +70,8 @@ module VirtualDispatch {
|
||||
* that is, `c` or one of its supertypes overrides `f`.
|
||||
*/
|
||||
private predicate cannotInherit(Class c, MemberFunction f) {
|
||||
exists(MemberFunction override |
|
||||
cannotInheritHelper(c, f, _, override) and
|
||||
exists(Class overridingType, MemberFunction override |
|
||||
cannotInheritHelper(c, f, overridingType, override) and
|
||||
override.overrides+(f)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class Expr extends StmtParent, @expr {
|
||||
Declaration getEnclosingDeclaration() { result = exprEnclosingElement(this) }
|
||||
|
||||
/** Gets a child of this expression. */
|
||||
Expr getAChild() { result = this.getChild(_) }
|
||||
Expr getAChild() { exists(int n | result = this.getChild(n)) }
|
||||
|
||||
/** Gets the parent of this expression, if any. */
|
||||
Element getParent() { exprparents(underlyingElement(this), _, unresolveElement(result)) }
|
||||
|
||||
@@ -1,21 +1,643 @@
|
||||
/**
|
||||
* DEPRECATED: Use `semmle.code.cpp.ir.dataflow.TaintTracking` as a replacement.
|
||||
*
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl as DefaultTaintTrackingImpl
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.ResolveCall
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking2
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking3
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
|
||||
deprecated predicate predictableOnlyFlow = DefaultTaintTrackingImpl::predictableOnlyFlow/1;
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
private predicate predictableInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
// This could be a conversion on a string literal
|
||||
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
deprecated predicate tainted = DefaultTaintTrackingImpl::tainted/2;
|
||||
/**
|
||||
* Functions that we should only allow taint to flow through (to the return
|
||||
* value) if all but the source argument are 'predictable'. This is done to
|
||||
* emulate the old security library's implementation rather than due to any
|
||||
* strong belief that this is the right approach.
|
||||
*
|
||||
* Note that the list itself is not very principled; it consists of all the
|
||||
* functions listed in the old security library's [default] `isPureFunction`
|
||||
* that have more than one argument, but are not in the old taint tracking
|
||||
* library's `returnArgument` predicate.
|
||||
*/
|
||||
predicate predictableOnlyFlow(string name) {
|
||||
name =
|
||||
[
|
||||
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
|
||||
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
|
||||
"strtoul"
|
||||
]
|
||||
}
|
||||
|
||||
deprecated predicate taintedIncludingGlobalVars =
|
||||
DefaultTaintTrackingImpl::taintedIncludingGlobalVars/3;
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
result = getNodeForExpr(source)
|
||||
}
|
||||
|
||||
deprecated predicate globalVarFromId = DefaultTaintTrackingImpl::globalVarFromId/1;
|
||||
private DataFlow::Node getNodeForExpr(Expr node) {
|
||||
result = DataFlow::exprNode(node)
|
||||
or
|
||||
// Some of the sources in `isUserInput` are intended to match the value of
|
||||
// an expression, while others (those modeled below) are intended to match
|
||||
// the taint that propagates out of an argument, like the `char *` argument
|
||||
// to `gets`. It's impossible here to tell which is which, but the "access
|
||||
// to argv" source is definitely not intended to match an output argument,
|
||||
// and it causes false positives if we let it.
|
||||
//
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `nodeIsBarrierIn`.
|
||||
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
|
||||
not argv(node.(VariableAccess).getTarget())
|
||||
}
|
||||
|
||||
deprecated module TaintedWithPath = DefaultTaintTrackingImpl::TaintedWithPath;
|
||||
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
|
||||
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// This set of sources should be reasonably small, which is good for
|
||||
// performance since the set of sinks is very large.
|
||||
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Additional step for flow out of variables. There is no flow _into_
|
||||
// variables in this configuration, so this step only serves to take flow
|
||||
// out of a variable that's a source.
|
||||
readsVariable(n2.asInstruction(), n1.asVariable())
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that has any kind of upper-bound check anywhere in the program. This is
|
||||
* biased towards being inclusive because there are a lot of valid ways of doing an
|
||||
* upper bounds checks if we don't consider where it occurs, for example:
|
||||
* ```
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
// TODO: This coarse overapproximation, ported from the old taint tracking
|
||||
// library, could be replaced with an actual semantic check that a particular
|
||||
// variable _access_ is guarded by an upper-bound check. We probably don't want
|
||||
// to do this right away since it could expose a lot of FPs that were
|
||||
// previously suppressed by this predicate by coincidence.
|
||||
private predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate nodeIsBarrierEqualityCandidate(
|
||||
DataFlow::Node node, Operand access, Variable checkedVar
|
||||
) {
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
exists(Variable checkedVar |
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
exists(Variable checkedVar, Operand access |
|
||||
/*
|
||||
* This node is guarded by a condition that forces the accessed variable
|
||||
* to equal something else. For example:
|
||||
* ```
|
||||
* x = taintsource()
|
||||
* if (x == 10) {
|
||||
* taintsink(x); // not considered tainted
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||
exists(Expr source | isUserInput(source, _) |
|
||||
node = DataFlow::exprNode(source)
|
||||
or
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `getNodeForSource`.
|
||||
node = DataFlow::definitionByReferenceNodeFromArgument(source)
|
||||
)
|
||||
or
|
||||
// don't use dataflow into binary instructions if both operands are unpredictable
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not predictableInstruction(iTo.getLeft()) and
|
||||
not predictableInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of predictability
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// don't use dataflow through calls to pure functions if two or more operands
|
||||
// are unpredictable
|
||||
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
isPureFunction(iTo.getStaticCallTarget().getName()) and
|
||||
iFrom1 = iTo.getAnArgument() and
|
||||
iFrom2 = iTo.getAnArgument() and
|
||||
not predictableInstruction(iFrom1) and
|
||||
not predictableInstruction(iFrom2) and
|
||||
iFrom1 != iFrom2
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Element adjustedSink(DataFlow::Node sink) {
|
||||
// TODO: is it more appropriate to use asConvertedExpr here and avoid
|
||||
// `getConversion*`? Or will that cause us to miss some cases where there's
|
||||
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
|
||||
// pretend there was flow to the converted `Expr` for the sake of
|
||||
// compatibility.
|
||||
sink.asExpr().getConversion*() = result
|
||||
or
|
||||
// For compatibility, send flow from arguments to parameters, even for
|
||||
// functions with no body.
|
||||
exists(FunctionCall call, int i |
|
||||
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
|
||||
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `Variable` if there is flow to any
|
||||
// Load or Store of that variable.
|
||||
exists(CopyInstruction copy |
|
||||
copy.getSourceValue() = sink.asInstruction() and
|
||||
(
|
||||
readsVariable(copy, result) or
|
||||
writesVariable(copy, result)
|
||||
) and
|
||||
not hasUpperBoundsCheck(result)
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `NotExpr` even if it's part of a
|
||||
// short-circuiting condition and thus might get skipped.
|
||||
result.(NotExpr).getOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint postfix and prefix crement operations when their operand is tainted.
|
||||
result.(CrementOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
|
||||
result.(AssignOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
result =
|
||||
sink.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to return value of a modeled function when an input taints the
|
||||
* dereference of the return value.
|
||||
*/
|
||||
cached
|
||||
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
n1.asOperand() = callInput(call, modelIn) and
|
||||
(
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
) and
|
||||
call.getStaticCallTarget() = func and
|
||||
modelOut.isReturnValueDeref() and
|
||||
call = n2.asInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call `taintedIncludingGlobalVars`.
|
||||
*/
|
||||
cached
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
|
||||
cfg.hasFlow(getNodeForSource(source), sink) and
|
||||
tainted = adjustedSink(sink)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where the taint passed
|
||||
* through a global variable named `globalVar`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This version gives the same results as tainted but also includes
|
||||
* data flow through global variables.
|
||||
*
|
||||
* The parameter `globalVar` is the qualified name of the last global variable
|
||||
* used to move the value from source to tainted. If the taint did not pass
|
||||
* through a global variable, then `globalVar = ""`.
|
||||
*/
|
||||
cached
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
tainted(source, tainted) and
|
||||
globalVar = ""
|
||||
or
|
||||
exists(
|
||||
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
|
||||
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
||||
|
|
||||
global = variableNode.getVariable() and
|
||||
toCfg.hasFlow(getNodeForSource(source), variableNode) and
|
||||
fromCfg.hasFlow(variableNode, sink) and
|
||||
tainted = adjustedSink(sink) and
|
||||
global = globalVarFromId(globalVar)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global variable whose qualified name is `id`. Use this predicate
|
||||
* together with `taintedIncludingGlobalVars`. Example:
|
||||
*
|
||||
* ```
|
||||
* exists(string varName |
|
||||
* taintedIncludingGlobalVars(source, tainted, varName) and
|
||||
* var = globalVarFromId(varName)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Provides definitions for augmenting source/sink pairs with data-flow paths
|
||||
* between them. From a `@kind path-problem` query, import this module in the
|
||||
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
|
||||
* in place of `tainted`.
|
||||
*
|
||||
* Importing this module will also import the query predicates that contain the
|
||||
* taint paths.
|
||||
*/
|
||||
module TaintedWithPath {
|
||||
private newtype TSingleton = MkSingleton()
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that matches sources and sinks in the same
|
||||
* way as the `tainted` predicate.
|
||||
*
|
||||
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
|
||||
* a characteristic predicate.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TSingleton {
|
||||
/** Override this to specify which elements are sources in this configuration. */
|
||||
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
|
||||
|
||||
/** Override this to specify which elements are sinks in this configuration. */
|
||||
abstract predicate isSink(Element e);
|
||||
|
||||
/** Override this to specify which expressions are barriers in this configuration. */
|
||||
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
|
||||
|
||||
/**
|
||||
* Override this predicate to `any()` to allow taint to flow through global
|
||||
* variables.
|
||||
*/
|
||||
predicate taintThroughGlobals() { none() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TaintTrackingConfiguration" }
|
||||
}
|
||||
|
||||
private class AdjustedConfiguration extends TaintTracking3::Configuration {
|
||||
AdjustedConfiguration() { this = "AdjustedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e |
|
||||
cfg.isSource(e) and source = getNodeForExpr(e)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Steps into and out of global variables
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
)
|
||||
or
|
||||
additionalTaintStep(n1, n2)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
/*
|
||||
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
|
||||
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
|
||||
* conversions, and a `Variable` maps to all loads and stores from it. Because
|
||||
* the path node is part of the tuple that constitutes the alert, this leads
|
||||
* to duplicate alerts.
|
||||
*
|
||||
* To avoid showing duplicates, we edit the graph to replace the final node
|
||||
* coming from the data-flow library with a node that matches exactly the
|
||||
* `Element` sink that's requested.
|
||||
*
|
||||
* The same is done for sources.
|
||||
*/
|
||||
|
||||
private newtype TPathNode =
|
||||
TWrapPathNode(DataFlow3::PathNode n) or
|
||||
// There's a single newtype constructor for both sources and sinks since
|
||||
// that makes it easiest to deal with the case where source = sink.
|
||||
TEndpointPathNode(Element e) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
|
||||
cfg.hasFlow(sourceNode, sinkNode)
|
||||
|
|
||||
sourceNode = getNodeForExpr(e) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
|
||||
or
|
||||
e = adjustedSink(sinkNode) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
|
||||
)
|
||||
}
|
||||
|
||||
/** An opaque type used for the nodes of a data-flow path. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
module Private {
|
||||
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
|
||||
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
|
||||
|
||||
/** Gets the element that `pathNode` wraps, if any. */
|
||||
Element getElementFromPathNode(PathNode pathNode) {
|
||||
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
|
||||
result = node.asInstruction().getAst()
|
||||
or
|
||||
result = node.asOperand().getDef().getAst()
|
||||
)
|
||||
or
|
||||
result = pathNode.(EndpointPathNode).inner()
|
||||
}
|
||||
}
|
||||
|
||||
private class WrapPathNode extends PathNode, TWrapPathNode {
|
||||
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class EndpointPathNode extends PathNode, TEndpointPathNode {
|
||||
Expr inner() { this = TEndpointPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a source. It may also be a sink. */
|
||||
private class InitialPathNode extends EndpointPathNode {
|
||||
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a sink. It may also be a source. */
|
||||
private class FinalPathNode extends EndpointPathNode {
|
||||
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
|
||||
* from `par` to `ret` within it, in the graph of data flow path explanations.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
|
||||
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
|
||||
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
|
||||
* this predicate.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is computed from
|
||||
* user input in a way that users can probably control the exact output of
|
||||
* the computation.
|
||||
*/
|
||||
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
|
||||
source = sourceNode.(InitialPathNode).inner() and
|
||||
flowSource = getNodeForExpr(source) and
|
||||
cfg.hasFlow(flowSource, flowSink) and
|
||||
tainted = adjustedSink(flowSink) and
|
||||
tainted = sinkNode.(FinalPathNode).inner()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalVariablePathNode(WrapPathNode n) {
|
||||
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
|
||||
edges(a, b) and
|
||||
not isGlobalVariablePathNode(a) and
|
||||
not isGlobalVariablePathNode(b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` can be reached from a taint source without passing
|
||||
* through a global variable.
|
||||
*/
|
||||
predicate taintedWithoutGlobals(Element tainted) {
|
||||
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
|
||||
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
|
||||
edgesWithoutGlobals+(sourceNode, sinkNode) and
|
||||
tainted = sinkNode.inner()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -916,56 +916,28 @@ private module Cached {
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParamNodeOption =
|
||||
TParamNodeNone() or
|
||||
TParamNodeSome(ParamNode p)
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
TAccessPathFrontNone() or
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFrontOption =
|
||||
TApproxAccessPathFrontNone() or
|
||||
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1343,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParamNode`. */
|
||||
class ParamNodeOption extends TParamNodeOption {
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParamNodeNone() and
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParamNode p |
|
||||
this = TParamNodeSome(p) and
|
||||
result = p.toString()
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1363,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption {
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
@@ -1374,82 +1346,13 @@ class ReturnCtx extends TReturnCtx {
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
this = TReturnCtxMaybeFlowThrough(pos) and
|
||||
result = pos.toString()
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional approximated access path front. */
|
||||
class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
string toString() {
|
||||
this = TApproxAccessPathFrontNone() and result = "<none>"
|
||||
or
|
||||
this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
@@ -1482,7 +1385,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract ApproxAccessPathFront toApprox();
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
@@ -1496,7 +1399,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
@@ -1508,7 +1411,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -101,7 +101,9 @@ module Consistency {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
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
|
||||
@@ -258,9 +260,4 @@ module Consistency {
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
|
||||
query predicate uniqueContentApprox(Content c, string msg) {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,13 +400,6 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox = Unit;
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
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
|
||||
|
||||
@@ -1,644 +0,0 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.ResolveCall
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking2
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking3
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
private predicate predictableInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
// This could be a conversion on a string literal
|
||||
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions that we should only allow taint to flow through (to the return
|
||||
* value) if all but the source argument are 'predictable'. This is done to
|
||||
* emulate the old security library's implementation rather than due to any
|
||||
* strong belief that this is the right approach.
|
||||
*
|
||||
* Note that the list itself is not very principled; it consists of all the
|
||||
* functions listed in the old security library's [default] `isPureFunction`
|
||||
* that have more than one argument, but are not in the old taint tracking
|
||||
* library's `returnArgument` predicate.
|
||||
*/
|
||||
predicate predictableOnlyFlow(string name) {
|
||||
name =
|
||||
[
|
||||
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
|
||||
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
|
||||
"strtoul"
|
||||
]
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
result = getNodeForExpr(source)
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForExpr(Expr node) {
|
||||
result = DataFlow::exprNode(node)
|
||||
or
|
||||
// Some of the sources in `isUserInput` are intended to match the value of
|
||||
// an expression, while others (those modeled below) are intended to match
|
||||
// the taint that propagates out of an argument, like the `char *` argument
|
||||
// to `gets`. It's impossible here to tell which is which, but the "access
|
||||
// to argv" source is definitely not intended to match an output argument,
|
||||
// and it causes false positives if we let it.
|
||||
//
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `nodeIsBarrierIn`.
|
||||
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
|
||||
not argv(node.(VariableAccess).getTarget())
|
||||
}
|
||||
|
||||
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
|
||||
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// This set of sources should be reasonably small, which is good for
|
||||
// performance since the set of sinks is very large.
|
||||
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Additional step for flow out of variables. There is no flow _into_
|
||||
// variables in this configuration, so this step only serves to take flow
|
||||
// out of a variable that's a source.
|
||||
readsVariable(n2.asInstruction(), n1.asVariable())
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that has any kind of upper-bound check anywhere in the program. This is
|
||||
* biased towards being inclusive because there are a lot of valid ways of doing an
|
||||
* upper bounds checks if we don't consider where it occurs, for example:
|
||||
* ```
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
// TODO: This coarse overapproximation, ported from the old taint tracking
|
||||
// library, could be replaced with an actual semantic check that a particular
|
||||
// variable _access_ is guarded by an upper-bound check. We probably don't want
|
||||
// to do this right away since it could expose a lot of FPs that were
|
||||
// previously suppressed by this predicate by coincidence.
|
||||
private predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate nodeIsBarrierEqualityCandidate(
|
||||
DataFlow::Node node, Operand access, Variable checkedVar
|
||||
) {
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
exists(Variable checkedVar |
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
exists(Variable checkedVar, Operand access |
|
||||
/*
|
||||
* This node is guarded by a condition that forces the accessed variable
|
||||
* to equal something else. For example:
|
||||
* ```
|
||||
* x = taintsource()
|
||||
* if (x == 10) {
|
||||
* taintsink(x); // not considered tainted
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||
exists(Expr source | isUserInput(source, _) |
|
||||
node = DataFlow::exprNode(source)
|
||||
or
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `getNodeForSource`.
|
||||
node = DataFlow::definitionByReferenceNodeFromArgument(source)
|
||||
)
|
||||
or
|
||||
// don't use dataflow into binary instructions if both operands are unpredictable
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not predictableInstruction(iTo.getLeft()) and
|
||||
not predictableInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of predictability
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// don't use dataflow through calls to pure functions if two or more operands
|
||||
// are unpredictable
|
||||
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
isPureFunction(iTo.getStaticCallTarget().getName()) and
|
||||
iFrom1 = iTo.getAnArgument() and
|
||||
iFrom2 = iTo.getAnArgument() and
|
||||
not predictableInstruction(iFrom1) and
|
||||
not predictableInstruction(iFrom2) and
|
||||
iFrom1 != iFrom2
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Element adjustedSink(DataFlow::Node sink) {
|
||||
// TODO: is it more appropriate to use asConvertedExpr here and avoid
|
||||
// `getConversion*`? Or will that cause us to miss some cases where there's
|
||||
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
|
||||
// pretend there was flow to the converted `Expr` for the sake of
|
||||
// compatibility.
|
||||
sink.asExpr().getConversion*() = result
|
||||
or
|
||||
// For compatibility, send flow from arguments to parameters, even for
|
||||
// functions with no body.
|
||||
exists(FunctionCall call, int i |
|
||||
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
|
||||
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `Variable` if there is flow to any
|
||||
// Load or Store of that variable.
|
||||
exists(CopyInstruction copy |
|
||||
copy.getSourceValue() = sink.asInstruction() and
|
||||
(
|
||||
readsVariable(copy, result) or
|
||||
writesVariable(copy, result)
|
||||
) and
|
||||
not hasUpperBoundsCheck(result)
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `NotExpr` even if it's part of a
|
||||
// short-circuiting condition and thus might get skipped.
|
||||
result.(NotExpr).getOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint postfix and prefix crement operations when their operand is tainted.
|
||||
result.(CrementOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
|
||||
result.(AssignOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
result =
|
||||
sink.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to return value of a modeled function when an input taints the
|
||||
* dereference of the return value.
|
||||
*/
|
||||
cached
|
||||
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
n1.asOperand() = callInput(call, modelIn) and
|
||||
(
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
) and
|
||||
call.getStaticCallTarget() = func and
|
||||
modelOut.isReturnValueDeref() and
|
||||
call = n2.asInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call `taintedIncludingGlobalVars`.
|
||||
*/
|
||||
cached
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
|
||||
cfg.hasFlow(getNodeForSource(source), sink) and
|
||||
tainted = adjustedSink(sink)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where the taint passed
|
||||
* through a global variable named `globalVar`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This version gives the same results as tainted but also includes
|
||||
* data flow through global variables.
|
||||
*
|
||||
* The parameter `globalVar` is the qualified name of the last global variable
|
||||
* used to move the value from source to tainted. If the taint did not pass
|
||||
* through a global variable, then `globalVar = ""`.
|
||||
*/
|
||||
cached
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
tainted(source, tainted) and
|
||||
globalVar = ""
|
||||
or
|
||||
exists(
|
||||
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
|
||||
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
||||
|
|
||||
global = variableNode.getVariable() and
|
||||
toCfg.hasFlow(getNodeForSource(source), variableNode) and
|
||||
fromCfg.hasFlow(variableNode, sink) and
|
||||
tainted = adjustedSink(sink) and
|
||||
global = globalVarFromId(globalVar)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global variable whose qualified name is `id`. Use this predicate
|
||||
* together with `taintedIncludingGlobalVars`. Example:
|
||||
*
|
||||
* ```
|
||||
* exists(string varName |
|
||||
* taintedIncludingGlobalVars(source, tainted, varName) and
|
||||
* var = globalVarFromId(varName)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Provides definitions for augmenting source/sink pairs with data-flow paths
|
||||
* between them. From a `@kind path-problem` query, import this module in the
|
||||
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
|
||||
* in place of `tainted`.
|
||||
*
|
||||
* Importing this module will also import the query predicates that contain the
|
||||
* taint paths.
|
||||
*/
|
||||
module TaintedWithPath {
|
||||
private newtype TSingleton = MkSingleton()
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that matches sources and sinks in the same
|
||||
* way as the `tainted` predicate.
|
||||
*
|
||||
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
|
||||
* a characteristic predicate.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TSingleton {
|
||||
/** Override this to specify which elements are sources in this configuration. */
|
||||
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
|
||||
|
||||
/** Override this to specify which elements are sinks in this configuration. */
|
||||
abstract predicate isSink(Element e);
|
||||
|
||||
/** Override this to specify which expressions are barriers in this configuration. */
|
||||
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
|
||||
|
||||
/**
|
||||
* Override this predicate to `any()` to allow taint to flow through global
|
||||
* variables.
|
||||
*/
|
||||
predicate taintThroughGlobals() { none() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TaintTrackingConfiguration" }
|
||||
}
|
||||
|
||||
private class AdjustedConfiguration extends TaintTracking3::Configuration {
|
||||
AdjustedConfiguration() { this = "AdjustedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e |
|
||||
cfg.isSource(e) and source = getNodeForExpr(e)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Steps into and out of global variables
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
)
|
||||
or
|
||||
additionalTaintStep(n1, n2)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
/*
|
||||
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
|
||||
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
|
||||
* conversions, and a `Variable` maps to all loads and stores from it. Because
|
||||
* the path node is part of the tuple that constitutes the alert, this leads
|
||||
* to duplicate alerts.
|
||||
*
|
||||
* To avoid showing duplicates, we edit the graph to replace the final node
|
||||
* coming from the data-flow library with a node that matches exactly the
|
||||
* `Element` sink that's requested.
|
||||
*
|
||||
* The same is done for sources.
|
||||
*/
|
||||
|
||||
private newtype TPathNode =
|
||||
TWrapPathNode(DataFlow3::PathNode n) or
|
||||
// There's a single newtype constructor for both sources and sinks since
|
||||
// that makes it easiest to deal with the case where source = sink.
|
||||
TEndpointPathNode(Element e) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
|
||||
cfg.hasFlow(sourceNode, sinkNode)
|
||||
|
|
||||
sourceNode = getNodeForExpr(e) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
|
||||
or
|
||||
e = adjustedSink(sinkNode) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
|
||||
)
|
||||
}
|
||||
|
||||
/** An opaque type used for the nodes of a data-flow path. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
module Private {
|
||||
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
|
||||
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
|
||||
|
||||
/** Gets the element that `pathNode` wraps, if any. */
|
||||
Element getElementFromPathNode(PathNode pathNode) {
|
||||
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
|
||||
result = node.asInstruction().getAst()
|
||||
or
|
||||
result = node.asOperand().getDef().getAst()
|
||||
)
|
||||
or
|
||||
result = pathNode.(EndpointPathNode).inner()
|
||||
}
|
||||
}
|
||||
|
||||
private class WrapPathNode extends PathNode, TWrapPathNode {
|
||||
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class EndpointPathNode extends PathNode, TEndpointPathNode {
|
||||
Expr inner() { this = TEndpointPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a source. It may also be a sink. */
|
||||
private class InitialPathNode extends EndpointPathNode {
|
||||
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a sink. It may also be a source. */
|
||||
private class FinalPathNode extends EndpointPathNode {
|
||||
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
|
||||
* from `par` to `ret` within it, in the graph of data flow path explanations.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
|
||||
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
|
||||
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
|
||||
* this predicate.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is computed from
|
||||
* user input in a way that users can probably control the exact output of
|
||||
* the computation.
|
||||
*/
|
||||
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
|
||||
source = sourceNode.(InitialPathNode).inner() and
|
||||
flowSource = getNodeForExpr(source) and
|
||||
cfg.hasFlow(flowSource, flowSink) and
|
||||
tainted = adjustedSink(flowSink) and
|
||||
tainted = sinkNode.(FinalPathNode).inner()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalVariablePathNode(WrapPathNode n) {
|
||||
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
|
||||
edges(a, b) and
|
||||
not isGlobalVariablePathNode(a) and
|
||||
not isGlobalVariablePathNode(b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` can be reached from a taint source without passing
|
||||
* through a global variable.
|
||||
*/
|
||||
predicate taintedWithoutGlobals(Element tainted) {
|
||||
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
|
||||
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
|
||||
edgesWithoutGlobals+(sourceNode, sinkNode) and
|
||||
tainted = sinkNode.inner()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ private newtype TOpcode =
|
||||
TNegate() or
|
||||
TShiftLeft() or
|
||||
TShiftRight() or
|
||||
TUnsignedShiftRight() or
|
||||
TBitAnd() or
|
||||
TBitOr() or
|
||||
TBitXor() or
|
||||
@@ -653,15 +652,6 @@ module Opcode {
|
||||
final override string toString() { result = "ShiftRight" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Opcode` for a `UnsignedShiftRightInstruction`.
|
||||
*
|
||||
* See the `UnsignedShiftRightInstruction` documentation for more details.
|
||||
*/
|
||||
class UnsignedShiftRight extends BinaryBitwiseOpcode, TUnsignedShiftRight {
|
||||
final override string toString() { result = "UnsignedShiftRight" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Opcode` for a `BitAndInstruction`.
|
||||
*
|
||||
|
||||
@@ -1204,17 +1204,6 @@ class ShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that shifts its left operand to the right by the number of bits specified by its
|
||||
* right operand.
|
||||
*
|
||||
* Both operands must have an integer type. The result has the same type as the left operand.
|
||||
* The leftmost bits are zero-filled.
|
||||
*/
|
||||
class UnsignedShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
UnsignedShiftRightInstruction() { this.getOpcode() instanceof Opcode::UnsignedShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that performs a binary arithmetic operation involving at least one pointer
|
||||
* operand.
|
||||
|
||||
@@ -45,7 +45,7 @@ class Operand extends TStageOperand {
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
this = chiOperand(_, _)
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
|
||||
@@ -329,12 +329,12 @@ private module Cached {
|
||||
cached
|
||||
Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock, int defRank,
|
||||
int defOffset, OldBlock useBlock, int useRank
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, _, defBlock, defRank, defOffset) and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar, _)
|
||||
|
||||
@@ -1204,17 +1204,6 @@ class ShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that shifts its left operand to the right by the number of bits specified by its
|
||||
* right operand.
|
||||
*
|
||||
* Both operands must have an integer type. The result has the same type as the left operand.
|
||||
* The leftmost bits are zero-filled.
|
||||
*/
|
||||
class UnsignedShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
UnsignedShiftRightInstruction() { this.getOpcode() instanceof Opcode::UnsignedShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that performs a binary arithmetic operation involving at least one pointer
|
||||
* operand.
|
||||
|
||||
@@ -45,7 +45,7 @@ class Operand extends TStageOperand {
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
this = chiOperand(_, _)
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
|
||||
@@ -72,19 +72,7 @@ newtype TInstructionTag =
|
||||
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) } or
|
||||
ThisAddressTag() or
|
||||
ThisLoadTag() or
|
||||
StructuredBindingAccessTag() or
|
||||
// The next three cases handle generation of the constants -1, 0 and 1 for __except handling.
|
||||
TryExceptGenerateNegativeOne() or
|
||||
TryExceptGenerateZero() or
|
||||
TryExceptGenerateOne() or
|
||||
// The next three cases handle generation of comparisons for __except handling.
|
||||
TryExceptCompareNegativeOne() or
|
||||
TryExceptCompareZero() or
|
||||
TryExceptCompareOne() or
|
||||
// The next three cases handle generation of branching for __except handling.
|
||||
TryExceptCompareNegativeOneBranch() or
|
||||
TryExceptCompareZeroBranch() or
|
||||
TryExceptCompareOneBranch()
|
||||
StructuredBindingAccessTag()
|
||||
|
||||
class InstructionTag extends TInstructionTag {
|
||||
final string toString() { result = "Tag" }
|
||||
@@ -236,22 +224,4 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = ThisLoadTag() and result = "ThisLoad"
|
||||
or
|
||||
tag = StructuredBindingAccessTag() and result = "StructuredBindingAccess"
|
||||
or
|
||||
tag = TryExceptCompareNegativeOne() and result = "TryExceptCompareNegativeOne"
|
||||
or
|
||||
tag = TryExceptCompareZero() and result = "TryExceptCompareZero"
|
||||
or
|
||||
tag = TryExceptCompareOne() and result = "TryExceptCompareOne"
|
||||
or
|
||||
tag = TryExceptGenerateNegativeOne() and result = "TryExceptGenerateNegativeOne"
|
||||
or
|
||||
tag = TryExceptGenerateZero() and result = "TryExceptGenerateNegativeOne"
|
||||
or
|
||||
tag = TryExceptGenerateOne() and result = "TryExceptGenerateOne"
|
||||
or
|
||||
tag = TryExceptCompareNegativeOneBranch() and result = "TryExceptCompareNegativeOneBranch"
|
||||
or
|
||||
tag = TryExceptCompareZeroBranch() and result = "TryExceptCompareZeroBranch"
|
||||
or
|
||||
tag = TryExceptCompareOneBranch() and result = "TryExceptCompareOneBranch"
|
||||
}
|
||||
|
||||
@@ -675,7 +675,6 @@ newtype TTranslatedElement =
|
||||
} or
|
||||
// A statement
|
||||
TTranslatedStmt(Stmt stmt) { translateStmt(stmt) } or
|
||||
TTranslatedMicrosoftTryExceptHandler(MicrosoftTryExceptStmt stmt) or
|
||||
// A function
|
||||
TTranslatedFunction(Function func) { translateFunction(func) } or
|
||||
// A constructor init list
|
||||
|
||||
@@ -298,11 +298,11 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
or
|
||||
exists(int elementCount |
|
||||
exists(int startIndex, int elementCount |
|
||||
// If the initializer string isn't large enough to fill the target, then
|
||||
// we have to generate another instruction sequence to store a constant
|
||||
// zero into the remainder of the array.
|
||||
zeroInitRange(_, elementCount) and
|
||||
zeroInitRange(startIndex, elementCount) and
|
||||
(
|
||||
// Create a constant zero whose size is the size of the remaining
|
||||
// space in the target array.
|
||||
|
||||
@@ -13,222 +13,6 @@ private import TranslatedInitialization
|
||||
|
||||
TranslatedStmt getTranslatedStmt(Stmt stmt) { result.getAst() = stmt }
|
||||
|
||||
TranslatedMicrosoftTryExceptHandler getTranslatedMicrosoftTryExceptHandler(
|
||||
MicrosoftTryExceptStmt tryExcept
|
||||
) {
|
||||
result.getAst() = tryExcept.getExcept()
|
||||
}
|
||||
|
||||
class TranslatedMicrosoftTryExceptHandler extends TranslatedElement,
|
||||
TTranslatedMicrosoftTryExceptHandler {
|
||||
MicrosoftTryExceptStmt tryExcept;
|
||||
|
||||
TranslatedMicrosoftTryExceptHandler() { this = TTranslatedMicrosoftTryExceptHandler(tryExcept) }
|
||||
|
||||
final override string toString() { result = tryExcept.toString() }
|
||||
|
||||
final override Locatable getAst() { result = tryExcept.getExcept() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getChild(0).getFirstInstruction() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
// t1 = -1
|
||||
tag = TryExceptGenerateNegativeOne() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getIntType()
|
||||
or
|
||||
// t2 = cmp t1, condition
|
||||
tag = TryExceptCompareNegativeOne() and
|
||||
opcode instanceof Opcode::CompareEQ and
|
||||
resultType = getBoolType()
|
||||
or
|
||||
// if t2 goto ... else goto ...
|
||||
tag = TryExceptCompareNegativeOneBranch() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
// t1 = 0
|
||||
tag = TryExceptGenerateZero() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getIntType()
|
||||
or
|
||||
// t2 = cmp t1, condition
|
||||
tag = TryExceptCompareZero() and
|
||||
opcode instanceof Opcode::CompareEQ and
|
||||
resultType = getBoolType()
|
||||
or
|
||||
// if t2 goto ... else goto ...
|
||||
tag = TryExceptCompareZeroBranch() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
// t1 = 1
|
||||
tag = TryExceptGenerateOne() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getIntType()
|
||||
or
|
||||
// t2 = cmp t1, condition
|
||||
tag = TryExceptCompareOne() and
|
||||
opcode instanceof Opcode::CompareEQ and
|
||||
resultType = getBoolType()
|
||||
or
|
||||
// if t2 goto ... else goto ...
|
||||
tag = TryExceptCompareOneBranch() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
// unwind stack
|
||||
tag = UnwindTag() and
|
||||
opcode instanceof Opcode::Unwind and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = TryExceptCompareNegativeOne() and
|
||||
(
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = this.getTranslatedCondition().getResult()
|
||||
or
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = this.getInstruction(TryExceptGenerateNegativeOne())
|
||||
)
|
||||
or
|
||||
tag = TryExceptCompareNegativeOneBranch() and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = this.getInstruction(TryExceptCompareNegativeOne())
|
||||
or
|
||||
tag = TryExceptCompareZero() and
|
||||
(
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = this.getTranslatedCondition().getResult()
|
||||
or
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = this.getInstruction(TryExceptGenerateZero())
|
||||
)
|
||||
or
|
||||
tag = TryExceptCompareZeroBranch() and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = this.getInstruction(TryExceptCompareZero())
|
||||
or
|
||||
tag = TryExceptCompareOne() and
|
||||
(
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = this.getTranslatedCondition().getResult()
|
||||
or
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = this.getInstruction(TryExceptGenerateOne())
|
||||
)
|
||||
or
|
||||
tag = TryExceptCompareOneBranch() and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = this.getInstruction(TryExceptCompareOne())
|
||||
}
|
||||
|
||||
override string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = TryExceptGenerateNegativeOne() and
|
||||
result = "-1"
|
||||
or
|
||||
tag = TryExceptGenerateZero() and
|
||||
result = "0"
|
||||
or
|
||||
tag = TryExceptGenerateOne() and
|
||||
result = "1"
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
// Generate -1 -> Compare condition
|
||||
tag = TryExceptGenerateNegativeOne() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(TryExceptCompareNegativeOne())
|
||||
or
|
||||
// Compare condition -> Branch
|
||||
tag = TryExceptCompareNegativeOne() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(TryExceptCompareNegativeOneBranch())
|
||||
or
|
||||
// Branch -> Unwind or Generate 0
|
||||
tag = TryExceptCompareNegativeOneBranch() and
|
||||
(
|
||||
kind instanceof TrueEdge and
|
||||
// TODO: This is not really correct. The semantics of `EXCEPTION_CONTINUE_EXECUTION` is that
|
||||
// we should continue execution at the point where the exception occurred. But we don't have
|
||||
// any instruction to model this behavior.
|
||||
result = this.getInstruction(UnwindTag())
|
||||
or
|
||||
kind instanceof FalseEdge and
|
||||
result = this.getInstruction(TryExceptGenerateZero())
|
||||
)
|
||||
or
|
||||
// Generate 0 -> Compare condition
|
||||
tag = TryExceptGenerateZero() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(TryExceptCompareZero())
|
||||
or
|
||||
// Compare condition -> Branch
|
||||
tag = TryExceptCompareZero() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(TryExceptCompareZeroBranch())
|
||||
or
|
||||
// Branch -> Unwind or Generate 1
|
||||
tag = TryExceptCompareZeroBranch() and
|
||||
(
|
||||
kind instanceof TrueEdge and
|
||||
result = this.getInstruction(UnwindTag())
|
||||
or
|
||||
kind instanceof FalseEdge and
|
||||
result = this.getInstruction(TryExceptGenerateOne())
|
||||
)
|
||||
or
|
||||
// Generate 1 -> Compare condition
|
||||
tag = TryExceptGenerateOne() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(TryExceptCompareOne())
|
||||
or
|
||||
// Compare condition -> Branch
|
||||
tag = TryExceptCompareOne() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(TryExceptCompareOneBranch())
|
||||
or
|
||||
// Branch -> Handler (the condition value is always 0, -1 or 1, and we've checked for 0 or -1 already.)
|
||||
tag = TryExceptCompareOneBranch() and
|
||||
(
|
||||
kind instanceof TrueEdge and
|
||||
result = this.getTranslatedHandler().getFirstInstruction()
|
||||
)
|
||||
or
|
||||
// Unwind -> Parent
|
||||
tag = UnwindTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getTranslatedCondition() and
|
||||
result = this.getInstruction(TryExceptGenerateNegativeOne())
|
||||
or
|
||||
child = this.getTranslatedHandler() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedCondition() {
|
||||
result = getTranslatedExpr(tryExcept.getCondition())
|
||||
}
|
||||
|
||||
private TranslatedStmt getTranslatedHandler() {
|
||||
result = getTranslatedStmt(tryExcept.getExcept())
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and
|
||||
result = this.getTranslatedCondition()
|
||||
or
|
||||
id = 1 and
|
||||
result = this.getTranslatedHandler()
|
||||
}
|
||||
|
||||
final override Function getFunction() { result = tryExcept.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
|
||||
Stmt stmt;
|
||||
|
||||
@@ -465,57 +249,15 @@ class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ `try` statement, or a `__try __except` or `__try __finally` statement.
|
||||
*/
|
||||
private class TryOrMicrosoftTryStmt extends Stmt {
|
||||
TryOrMicrosoftTryStmt() {
|
||||
this instanceof TryStmt or
|
||||
this instanceof MicrosoftTryStmt
|
||||
}
|
||||
|
||||
/** Gets the number of `catch block`s of this statement. */
|
||||
int getNumberOfCatchClauses() {
|
||||
result = this.(TryStmt).getNumberOfCatchClauses()
|
||||
or
|
||||
this instanceof MicrosoftTryExceptStmt and
|
||||
result = 1
|
||||
or
|
||||
this instanceof MicrosoftTryFinallyStmt and
|
||||
result = 0
|
||||
}
|
||||
|
||||
/** Gets the `body` statement of this statement. */
|
||||
Stmt getStmt() {
|
||||
result = this.(TryStmt).getStmt()
|
||||
or
|
||||
result = this.(MicrosoftTryStmt).getStmt()
|
||||
}
|
||||
|
||||
/** Gets the `i`th translated handler of this statement. */
|
||||
TranslatedElement getTranslatedHandler(int index) {
|
||||
result = getTranslatedStmt(this.(TryStmt).getChild(index + 1))
|
||||
or
|
||||
index = 0 and
|
||||
result = getTranslatedMicrosoftTryExceptHandler(this)
|
||||
}
|
||||
|
||||
/** Gets the `finally` statement (usually a BlockStmt), if any. */
|
||||
Stmt getFinally() { result = this.(MicrosoftTryFinallyStmt).getFinally() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a C++ `try` (or a `__try __except` or `__try __finally`) statement.
|
||||
* The IR translation of a C++ `try` statement.
|
||||
*/
|
||||
class TranslatedTryStmt extends TranslatedStmt {
|
||||
override TryOrMicrosoftTryStmt stmt;
|
||||
override TryStmt stmt;
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getBody()
|
||||
or
|
||||
result = getHandler(id - 1)
|
||||
or
|
||||
id = stmt.getNumberOfCatchClauses() + 1 and
|
||||
result = this.getFinally()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
@@ -527,20 +269,8 @@ class TranslatedTryStmt extends TranslatedStmt {
|
||||
override Instruction getFirstInstruction() { result = getBody().getFirstInstruction() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
// All non-finally children go to the successor of the `try` if
|
||||
// there is no finally block, but if there is a finally block
|
||||
// then we go to that one.
|
||||
child = [this.getBody(), this.getHandler(_)] and
|
||||
(
|
||||
not exists(this.getFinally()) and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
or
|
||||
result = this.getFinally().getFirstInstruction()
|
||||
)
|
||||
or
|
||||
// And after the finally block we go to the successor of the `try`.
|
||||
child = this.getFinally() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
// All children go to the successor of the `try`.
|
||||
child = getAChild() and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final Instruction getNextHandler(TranslatedHandler handler) {
|
||||
@@ -560,9 +290,9 @@ class TranslatedTryStmt extends TranslatedStmt {
|
||||
result = getHandler(0).getFirstInstruction()
|
||||
}
|
||||
|
||||
private TranslatedElement getHandler(int index) { result = stmt.getTranslatedHandler(index) }
|
||||
|
||||
private TranslatedStmt getFinally() { result = getTranslatedStmt(stmt.getFinally()) }
|
||||
private TranslatedHandler getHandler(int index) {
|
||||
result = getTranslatedStmt(stmt.getChild(index + 1))
|
||||
}
|
||||
|
||||
private TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) }
|
||||
}
|
||||
|
||||
@@ -1204,17 +1204,6 @@ class ShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that shifts its left operand to the right by the number of bits specified by its
|
||||
* right operand.
|
||||
*
|
||||
* Both operands must have an integer type. The result has the same type as the left operand.
|
||||
* The leftmost bits are zero-filled.
|
||||
*/
|
||||
class UnsignedShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
UnsignedShiftRightInstruction() { this.getOpcode() instanceof Opcode::UnsignedShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that performs a binary arithmetic operation involving at least one pointer
|
||||
* operand.
|
||||
|
||||
@@ -45,7 +45,7 @@ class Operand extends TStageOperand {
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
this = chiOperand(_, _)
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
|
||||
@@ -329,12 +329,12 @@ private module Cached {
|
||||
cached
|
||||
Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock, int defRank,
|
||||
int defOffset, OldBlock useBlock, int useRank
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, _, defBlock, defRank, defOffset) and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar, _)
|
||||
|
||||
@@ -67,7 +67,9 @@ class Class = Cpp::Class; // Used for inheritance conversions
|
||||
|
||||
predicate getIdentityString = Print::getIdentityString/1;
|
||||
|
||||
predicate hasCaseEdge(string minValue, string maxValue) { hasCaseEdge(_, minValue, maxValue) }
|
||||
predicate hasCaseEdge(string minValue, string maxValue) {
|
||||
exists(Cpp::SwitchCase switchCase | hasCaseEdge(switchCase, minValue, maxValue))
|
||||
}
|
||||
|
||||
predicate hasPositionalArgIndex(int argIndex) {
|
||||
exists(Cpp::FunctionCall call | exists(call.getArgument(argIndex))) or
|
||||
|
||||
@@ -99,10 +99,10 @@ class MetricClass extends Class {
|
||||
}
|
||||
|
||||
/** Gets any method that accesses some local field. */
|
||||
Function getAccessingMethod() { this.accessesLocalField(result, _) }
|
||||
Function getAccessingMethod() { exists(Field f | this.accessesLocalField(result, f)) }
|
||||
|
||||
/** Gets any field that is accessed by a local method. */
|
||||
Field getAccessedField() { this.accessesLocalField(_, result) }
|
||||
Field getAccessedField() { exists(Function func | this.accessesLocalField(func, result)) }
|
||||
|
||||
/** Gets the Henderson-Sellers lack-of-cohesion metric. */
|
||||
float getLackOfCohesionHS() {
|
||||
@@ -517,10 +517,10 @@ private predicate dependsOnClassSimple(Class source, Class dest) {
|
||||
)
|
||||
or
|
||||
// a class depends on classes for which a call to its member function is done from a function
|
||||
exists(MemberFunction target, MemberFunction f |
|
||||
exists(MemberFunction target, MemberFunction f, Locatable l |
|
||||
f.getDeclaringType() = source and
|
||||
f instanceof MemberFunction and
|
||||
f.calls(target, _) and
|
||||
f.calls(target, l) and
|
||||
target instanceof MemberFunction and
|
||||
target.getDeclaringType() = dest
|
||||
)
|
||||
|
||||
@@ -27,7 +27,7 @@ private import implementations.StdString
|
||||
private import implementations.Swap
|
||||
private import implementations.GetDelim
|
||||
private import implementations.SmartPointer
|
||||
private import implementations.Scanf
|
||||
private import implementations.Sscanf
|
||||
private import implementations.Send
|
||||
private import implementations.Recv
|
||||
private import implementations.Accept
|
||||
|
||||
@@ -15,6 +15,6 @@ private class Fread extends AliasFunction, RemoteFlowSourceFunction {
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,6 @@ private class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectF
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation class modeling the POSIX function `getenv` and
|
||||
* various similar functions.
|
||||
* Provides an implementation class modeling the POSIX function `getenv`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The POSIX function `getenv`, the GNU function `secure_getenv`, and the
|
||||
* Windows function `_wgetenv`.
|
||||
* The POSIX function `getenv`.
|
||||
*/
|
||||
class Getenv extends LocalFlowSourceFunction {
|
||||
Getenv() {
|
||||
this.hasGlobalOrStdOrBslName("getenv") or this.hasGlobalName(["secure_getenv", "_wgetenv"])
|
||||
}
|
||||
Getenv() { this.hasGlobalOrStdOrBslName("getenv") }
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
|
||||
@@ -49,10 +49,10 @@ private class FgetsFunction extends DataFlowFunction, TaintFunction, ArrayFuncti
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
or
|
||||
output.isReturnValue() and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
@@ -98,10 +98,10 @@ private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunctio
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
or
|
||||
output.isReturnValue() and
|
||||
description = "string read by " + this.getName()
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = 0 }
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
private class InetNtoa extends TaintFunction {
|
||||
InetNtoa() { this.hasGlobalName("inet_ntoa") }
|
||||
InetNtoa() { hasGlobalName("inet_ntoa") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -13,7 +12,7 @@ private class InetNtoa extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetAton extends TaintFunction, ArrayFunction {
|
||||
InetAton() { this.hasGlobalName("inet_aton") }
|
||||
InetAton() { hasGlobalName("inet_aton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -33,7 +32,7 @@ private class InetAton extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
InetAddr() { this.hasGlobalName("inet_addr") }
|
||||
InetAddr() { hasGlobalName("inet_addr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -52,7 +51,7 @@ private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
}
|
||||
|
||||
private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
InetNetwork() { this.hasGlobalName("inet_network") }
|
||||
InetNetwork() { hasGlobalName("inet_network") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -65,7 +64,7 @@ private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class InetMakeaddr extends TaintFunction {
|
||||
InetMakeaddr() { this.hasGlobalName("inet_makeaddr") }
|
||||
InetMakeaddr() { hasGlobalName("inet_makeaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -77,7 +76,7 @@ private class InetMakeaddr extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetLnaof extends TaintFunction {
|
||||
InetLnaof() { this.hasGlobalName("inet_lnaof") }
|
||||
InetLnaof() { hasGlobalName("inet_lnaof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -86,7 +85,7 @@ private class InetLnaof extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetNetof extends TaintFunction {
|
||||
InetNetof() { this.hasGlobalName("inet_netof") }
|
||||
InetNetof() { hasGlobalName("inet_netof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
@@ -95,7 +94,7 @@ private class InetNetof extends TaintFunction {
|
||||
}
|
||||
|
||||
private class InetPton extends TaintFunction, ArrayFunction {
|
||||
InetPton() { this.hasGlobalName("inet_pton") }
|
||||
InetPton() { hasGlobalName("inet_pton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -115,7 +114,7 @@ private class InetPton extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
Gethostbyname() { this.hasGlobalName("gethostbyname") }
|
||||
Gethostbyname() { hasGlobalName("gethostbyname") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
@@ -128,7 +127,7 @@ private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
private class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
Gethostbyaddr() { this.hasGlobalName("gethostbyaddr") }
|
||||
Gethostbyaddr() { hasGlobalName("gethostbyaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
@@ -143,21 +142,3 @@ private class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
private class Getaddrinfo extends TaintFunction, ArrayFunction, RemoteFlowSourceFunction {
|
||||
Getaddrinfo() { this.hasGlobalName("getaddrinfo") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref([0 .. 2]) and
|
||||
output.isParameterDeref(3)
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam in [0, 1] }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam in [0, 1] }
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(3) and
|
||||
description = "address returned by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ private class Recv extends AliasFunction, ArrayFunction, SideEffectFunction,
|
||||
or
|
||||
this.hasGlobalName("recvfrom") and output.isParameterDeref([4, 5])
|
||||
) and
|
||||
description = "buffer read by " + this.getName()
|
||||
description = "Buffer read by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
|
||||
|
||||
@@ -58,7 +58,7 @@ private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, Rem
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
|
||||
|
||||
override predicate hasRemoteFlowSink(FunctionInput input, string description) {
|
||||
input.isParameterDeref(1) and description = "buffer sent by " + this.getName()
|
||||
input.isParameterDeref(1) and description = "Buffer sent by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Provides implementation classes modeling the `scanf` family of functions.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
* Provides implementation classes modeling `sscanf`, `fscanf` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
@@ -9,15 +9,18 @@ import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The `scanf` family of functions.
|
||||
* The standard function `sscanf`, `fscanf` and its assorted variants
|
||||
*/
|
||||
abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, SideEffectFunction {
|
||||
SscanfModel() { this instanceof Sscanf or this instanceof Fscanf or this instanceof Snscanf }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
bufParam = this.(ScanfFunction).getFormatParameterIndex()
|
||||
or
|
||||
not this instanceof Fscanf and
|
||||
bufParam = this.(ScanfFunction).getInputParameterIndex()
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { this.hasArrayWithNullTerminator(bufParam) }
|
||||
@@ -33,7 +36,7 @@ abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction,
|
||||
)
|
||||
}
|
||||
|
||||
int getArgsStartPosition() { result = this.getNumberOfParameters() }
|
||||
private int getArgsStartPosition() { result = this.getNumberOfParameters() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and
|
||||
@@ -67,36 +70,3 @@ abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `scanf` and its assorted variants
|
||||
*/
|
||||
private class ScanfModel extends ScanfFunctionModel, LocalFlowSourceFunction instanceof Scanf {
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
|
||||
description = "value read by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `fscanf` and its assorted variants
|
||||
*/
|
||||
private class FscanfModel extends ScanfFunctionModel, RemoteFlowSourceFunction instanceof Fscanf {
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
|
||||
description = "value read by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `sscanf` and its assorted variants
|
||||
*/
|
||||
private class SscanfModel extends ScanfFunctionModel {
|
||||
SscanfModel() { this instanceof Sscanf or this instanceof Snscanf }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
super.hasArrayWithNullTerminator(bufParam)
|
||||
or
|
||||
bufParam = this.(ScanfFunction).getInputParameterIndex()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user