mirror of
https://github.com/github/codeql.git
synced 2026-05-25 00:27:09 +02:00
Compare commits
1 Commits
dbartol/fa
...
fossjunkie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc7736c324 |
8
.bazelrc
8
.bazelrc
@@ -1,9 +1,3 @@
|
||||
common --enable_platform_specific_config
|
||||
|
||||
build --repo_env=CC=clang --repo_env=CXX=clang++
|
||||
|
||||
build:linux --cxxopt=-std=c++20
|
||||
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
|
||||
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
|
||||
build --repo_env=CC=clang --repo_env=CXX=clang++ --cxxopt="-std=c++17"
|
||||
|
||||
try-import %workspace%/local.bazelrc
|
||||
|
||||
@@ -1 +1 @@
|
||||
6.3.1
|
||||
5.0.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extensions": [
|
||||
"rust-lang.rust-analyzer",
|
||||
"rust-lang.rust",
|
||||
"bungcip.better-toml",
|
||||
"github.vscode-codeql",
|
||||
"hbenl.vscode-test-explorer",
|
||||
|
||||
3
.github/labeler.yml
vendored
3
.github/labeler.yml
vendored
@@ -11,7 +11,7 @@ Go:
|
||||
- change-notes/**/*go.*
|
||||
|
||||
Java:
|
||||
- any: [ 'java/**/*', '!java/kotlin-extractor/**/*', '!java/ql/test/kotlin/**/*' ]
|
||||
- any: [ 'java/**/*', '!java/kotlin-extractor/**/*', '!java/kotlin-explorer/**/*', '!java/ql/test/kotlin/**/*' ]
|
||||
- change-notes/**/*java.*
|
||||
|
||||
JS:
|
||||
@@ -20,6 +20,7 @@ JS:
|
||||
|
||||
Kotlin:
|
||||
- java/kotlin-extractor/**/*
|
||||
- java/kotlin-explorer/**/*
|
||||
- java/ql/test/kotlin/**/*
|
||||
|
||||
Python:
|
||||
|
||||
102
.github/workflows/atm-check-query-suite.yml
vendored
Normal file
102
.github/workflows/atm-check-query-suite.yml
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
name: "ATM - Check query suite"
|
||||
|
||||
env:
|
||||
QUERY_PACK: javascript/ql/experimental/adaptivethreatmodeling/src
|
||||
QUERY_SUITE: codeql-suites/javascript-atm-code-scanning.qls
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/atm-check-query-suite.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
atm-check-query-suite:
|
||||
runs-on: ubuntu-latest-xl
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
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
|
||||
|
||||
# Install dependencies of ATM query pack, i.e. the ATM model
|
||||
codeql pack install "${QUERY_PACK}"
|
||||
|
||||
# Retrieve model checksum
|
||||
model_checksum=$(codeql resolve extensions "${QUERY_PACK}/${QUERY_SUITE}" | jq -r '.models[0].checksum')
|
||||
|
||||
# Trust the model so that we can use it in the ATM boosted queries
|
||||
mkdir -p "$HOME/.config/codeql"
|
||||
echo "--insecurely-execute-ml-model-checksums ${model_checksum}" >> "$HOME/.config/codeql/config"
|
||||
|
||||
- name: Create test DB
|
||||
run: |
|
||||
DB_PATH="${RUNNER_TEMP}/db"
|
||||
echo "DB_PATH=${DB_PATH}" >> "${GITHUB_ENV}"
|
||||
|
||||
codeql database create "${DB_PATH}" --source-root config/atm --language javascript
|
||||
|
||||
- name: Run ATM query suite
|
||||
run: |
|
||||
SARIF_PATH="${RUNNER_TEMP}/sarif.json"
|
||||
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}"
|
||||
|
||||
- name: Upload SARIF
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: javascript-ml-powered-queries.sarif
|
||||
path: "${{ env.SARIF_PATH }}"
|
||||
retention-days: 5
|
||||
|
||||
- name: Check results
|
||||
run: |
|
||||
# We should run at least the ML-powered queries in `expected_rules`.
|
||||
expected_rules="js/ml-powered/nosql-injection js/ml-powered/path-injection js/ml-powered/sql-injection js/ml-powered/xss"
|
||||
|
||||
for rule in ${expected_rules}; do
|
||||
found_rule=$(jq --arg rule "${rule}" '[.runs[0].tool.extensions[].rules | select(. != null) |
|
||||
flatten | .[].id] | any(. == $rule)' "${SARIF_PATH}")
|
||||
if [[ "${found_rule}" != "true" ]]; then
|
||||
echo "Expected SARIF output to contain rule '${rule}', but found no such rule."
|
||||
exit 1
|
||||
else
|
||||
echo "Found rule '${rule}'."
|
||||
fi
|
||||
done
|
||||
|
||||
# We should have at least one alert from an ML-powered query.
|
||||
num_alerts=$(jq '[.runs[0].results[] |
|
||||
select(.properties.score != null and (.rule.id | startswith("js/ml-powered/")))] | length' \
|
||||
"${SARIF_PATH}")
|
||||
if [[ "${num_alerts}" -eq 0 ]]; then
|
||||
echo "Expected to find at least one alert from an ML-powered query but found ${num_alerts}."
|
||||
exit 1
|
||||
else
|
||||
echo "Found ${num_alerts} alerts from ML-powered queries.";
|
||||
fi
|
||||
12
.github/workflows/atm-model-integration-tests.yml
vendored
Normal file
12
.github/workflows/atm-model-integration-tests.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
name: ATM Model Integration Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
hello-world:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: foo
|
||||
run: echo "Hello world"
|
||||
5
.github/workflows/check-change-note.yml
vendored
5
.github/workflows/check-change-note.yml
vendored
@@ -11,6 +11,7 @@ on:
|
||||
- "*/ql/lib/**/*.yml"
|
||||
- "!**/experimental/**"
|
||||
- "!ql/**"
|
||||
- "!swift/**"
|
||||
- ".github/workflows/check-change-note.yml"
|
||||
|
||||
jobs:
|
||||
@@ -26,9 +27,9 @@ 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', 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text, or released/x.y.z.md for released change-notes
|
||||
- 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$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$"))' |
|
||||
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
|
||||
|
||||
29
.github/workflows/check-implicit-this.yml
vendored
29
.github/workflows/check-implicit-this.yml
vendored
@@ -1,29 +0,0 @@
|
||||
name: "Check implicit this warnings"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**qlpack.yml"
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check that implicit this warnings is enabled for all packs
|
||||
shell: bash
|
||||
run: |
|
||||
EXIT_CODE=0
|
||||
packs="$(find . -iname 'qlpack.yml')"
|
||||
for pack_file in ${packs}; do
|
||||
option="$(yq '.warnOnImplicitThis' ${pack_file})"
|
||||
if [ "${option}" != "true" ]; then
|
||||
echo "::error file=${pack_file}::warnOnImplicitThis property must be set to 'true' for pack ${pack_file}"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done
|
||||
exit "${EXIT_CODE}"
|
||||
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@v8
|
||||
- uses: actions/stale@v7
|
||||
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,7 +10,6 @@ on:
|
||||
- "*/ql/src/**/*.qll"
|
||||
- "*/ql/lib/**/*.ql"
|
||||
- "*/ql/lib/**/*.qll"
|
||||
- "*/ql/lib/ext/**/*.yml"
|
||||
- "misc/scripts/library-coverage/*.py"
|
||||
# input data files
|
||||
- "*/documentation/library-coverage/cwe-sink.csv"
|
||||
|
||||
50
.github/workflows/fast-forward.yml
vendored
50
.github/workflows/fast-forward.yml
vendored
@@ -1,50 +0,0 @@
|
||||
# Fast-forwards the branch specified in BRANCH_NAME
|
||||
# to the github.ref/sha that this workflow is run on.
|
||||
# Used as part of the release process, to ensure
|
||||
# external query writers can always access a branch of github/codeql
|
||||
# that is compatible with the latest stable release.
|
||||
name: Fast-forward tracking branch for selected CodeQL version
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
fast-forward:
|
||||
name: Fast-forward tracking branch for selected CodeQL version
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'github/codeql'
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
BRANCH_NAME: 'lgtm.com'
|
||||
steps:
|
||||
- name: Validate chosen branch
|
||||
if: ${{ !startsWith(github.ref_name, 'codeql-cli-') }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::error ::The $BRANCH_NAME tracking branch should only be fast-forwarded to the tip of a codeql-cli-* branch, got $GITHUB_REF_NAME instead."
|
||||
exit 1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git config
|
||||
shell: bash
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Fetch
|
||||
shell: bash
|
||||
run: |
|
||||
set -x
|
||||
echo "Fetching $BRANCH_NAME"
|
||||
# Explicitly unshallow and fetch to ensure the remote ref is available.
|
||||
git fetch --unshallow origin "$BRANCH_NAME"
|
||||
git checkout -b "$BRANCH_NAME" "origin/$BRANCH_NAME"
|
||||
|
||||
- name: Fast-forward
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Fast-forwarding $BRANCH_NAME to ${GITHUB_REF}@${GITHUB_SHA}"
|
||||
git merge --ff-only "$GITHUB_SHA"
|
||||
git push origin "$BRANCH_NAME"
|
||||
10
.github/workflows/go-tests-other-os.yml
vendored
10
.github/workflows/go-tests-other-os.yml
vendored
@@ -7,17 +7,15 @@ on:
|
||||
- .github/workflows/go-tests-other-os.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
env:
|
||||
GO_VERSION: '~1.21.0'
|
||||
jobs:
|
||||
test-mac:
|
||||
name: Test MacOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
@@ -49,10 +47,10 @@ jobs:
|
||||
name: Test Windows
|
||||
runs-on: windows-latest-xl
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
|
||||
6
.github/workflows/go-tests.yml
vendored
6
.github/workflows/go-tests.yml
vendored
@@ -15,17 +15,15 @@ on:
|
||||
- .github/workflows/go-tests.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
env:
|
||||
GO_VERSION: '~1.21.0'
|
||||
jobs:
|
||||
test-linux:
|
||||
name: Test Linux (Ubuntu)
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
|
||||
2
.github/workflows/ql-for-ql-build.yml
vendored
2
.github/workflows/ql-for-ql-build.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
path: |
|
||||
ql/extractor-pack/
|
||||
ql/target/release/buramu
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ql/**/*.rs') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
|
||||
- name: Cache cargo
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
uses: actions/cache@v3
|
||||
|
||||
34
.github/workflows/ruby-build.yml
vendored
34
.github/workflows/ruby-build.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Install cargo-cross
|
||||
if: runner.os == 'Linux'
|
||||
run: cargo install cross --version 0.2.5
|
||||
run: cargo install cross --version 0.2.1
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- name: Cache entire extractor
|
||||
@@ -58,10 +58,12 @@ jobs:
|
||||
id: cache-extractor
|
||||
with:
|
||||
path: |
|
||||
ruby/extractor/target/release/codeql-extractor-ruby
|
||||
ruby/extractor/target/release/codeql-extractor-ruby.exe
|
||||
ruby/extractor/target/release/autobuilder
|
||||
ruby/extractor/target/release/autobuilder.exe
|
||||
ruby/extractor/target/release/extractor
|
||||
ruby/extractor/target/release/extractor.exe
|
||||
ruby/extractor/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}--${{ hashFiles('ruby/extractor/**/*.rs') }}
|
||||
- uses: actions/cache@v3
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
with:
|
||||
@@ -83,16 +85,13 @@ jobs:
|
||||
# This ensures we don't depend on glibc > 2.17.
|
||||
- name: Release build (linux)
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && runner.os == 'Linux'
|
||||
run: |
|
||||
cd extractor
|
||||
cross build --release
|
||||
mv target/x86_64-unknown-linux-gnu/release/codeql-extractor-ruby target/release/
|
||||
run: cd extractor && cross build --release
|
||||
- name: Release build (windows and macos)
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && runner.os != 'Linux'
|
||||
run: cd extractor && cargo build --release
|
||||
- name: Generate dbscheme
|
||||
if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
|
||||
run: extractor/target/release/codeql-extractor-ruby generate --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
run: extractor/target/release/generator --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
@@ -107,8 +106,10 @@ jobs:
|
||||
with:
|
||||
name: extractor-${{ matrix.os }}
|
||||
path: |
|
||||
ruby/extractor/target/release/codeql-extractor-ruby
|
||||
ruby/extractor/target/release/codeql-extractor-ruby.exe
|
||||
ruby/extractor/target/release/autobuilder
|
||||
ruby/extractor/target/release/autobuilder.exe
|
||||
ruby/extractor/target/release/extractor
|
||||
ruby/extractor/target/release/extractor.exe
|
||||
retention-days: 1
|
||||
compile-queries:
|
||||
runs-on: ubuntu-latest-xl
|
||||
@@ -166,10 +167,13 @@ jobs:
|
||||
mkdir -p ruby
|
||||
cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme.stats ruby/
|
||||
mkdir -p ruby/tools/{linux64,osx64,win64}
|
||||
cp linux64/codeql-extractor-ruby ruby/tools/linux64/extractor
|
||||
cp osx64/codeql-extractor-ruby ruby/tools/osx64/extractor
|
||||
cp win64/codeql-extractor-ruby.exe ruby/tools/win64/extractor.exe
|
||||
chmod +x ruby/tools/{linux64,osx64}/extractor
|
||||
cp linux64/autobuilder ruby/tools/linux64/autobuilder
|
||||
cp osx64/autobuilder ruby/tools/osx64/autobuilder
|
||||
cp win64/autobuilder.exe ruby/tools/win64/autobuilder.exe
|
||||
cp linux64/extractor ruby/tools/linux64/extractor
|
||||
cp osx64/extractor ruby/tools/osx64/extractor
|
||||
cp win64/extractor.exe ruby/tools/win64/extractor.exe
|
||||
chmod +x ruby/tools/{linux64,osx64}/{autobuilder,extractor}
|
||||
zip -rq codeql-ruby.zip ruby
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
1
.github/workflows/ruby-qltest.yml
vendored
1
.github/workflows/ruby-qltest.yml
vendored
@@ -4,7 +4,6 @@ on:
|
||||
push:
|
||||
paths:
|
||||
- "ruby/**"
|
||||
- "shared/**"
|
||||
- .github/workflows/ruby-build.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
|
||||
2
.github/workflows/swift.yml
vendored
2
.github/workflows/swift.yml
vendored
@@ -16,7 +16,6 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- rc/*
|
||||
- codeql-cli-*
|
||||
push:
|
||||
paths:
|
||||
- "swift/**"
|
||||
@@ -31,7 +30,6 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- rc/*
|
||||
- codeql-cli-*
|
||||
|
||||
jobs:
|
||||
# not using a matrix as you cannot depend on a specific job in a matrix, and we want to start linux checks
|
||||
|
||||
2
.github/workflows/sync-files.yml
vendored
2
.github/workflows/sync-files.yml
vendored
@@ -17,6 +17,4 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check synchronized files
|
||||
run: python config/sync-files.py
|
||||
- name: Check dbscheme fragments
|
||||
run: python config/sync-dbscheme-fragments.py
|
||||
|
||||
|
||||
46
.github/workflows/tree-sitter-extractor-test.yml
vendored
46
.github/workflows/tree-sitter-extractor-test.yml
vendored
@@ -1,46 +0,0 @@
|
||||
name: Test tree-sitter-extractor
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "shared/tree-sitter-extractor/**"
|
||||
- .github/workflows/tree-sitter-extractor-test.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- "shared/tree-sitter-extractor/**"
|
||||
- .github/workflows/tree-sitter-extractor-test.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: shared/tree-sitter-extractor
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check formatting
|
||||
run: cargo fmt --check
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run clippy
|
||||
run: cargo clippy -- --no-deps -D warnings -A clippy::new_without_default -A clippy::too_many_arguments
|
||||
@@ -5,9 +5,9 @@ repos:
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)|.*\.patch
|
||||
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
|
||||
- id: end-of-file-fixer
|
||||
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)|.*\.patch
|
||||
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v13.0.1
|
||||
@@ -19,12 +19,7 @@ repos:
|
||||
rev: v1.6.0
|
||||
hooks:
|
||||
- id: autopep8
|
||||
files: ^misc/codegen/.*\.py
|
||||
|
||||
- repo: https://github.com/warchant/pre-commit-buildifier
|
||||
rev: 0.0.2
|
||||
hooks:
|
||||
- id: buildifier
|
||||
files: ^swift/.*\.py
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
|
||||
18
.vscode/tasks.json
vendored
18
.vscode/tasks.json
vendored
@@ -22,22 +22,6 @@
|
||||
"command": "${config:python.pythonPath}",
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Accept .expected changes from CI",
|
||||
"type": "process",
|
||||
// Non-Windows OS will usually have Python 3 already installed at /usr/bin/python3.
|
||||
"command": "python3",
|
||||
"args": [
|
||||
"misc/scripts/accept-expected-changes-from-ci.py"
|
||||
],
|
||||
"group": "build",
|
||||
"windows": {
|
||||
// On Windows, use whatever Python interpreter is configured for this workspace. The default is
|
||||
// just `python`, so if Python is already on the path, this will find it.
|
||||
"command": "${config:python.pythonPath}",
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
/swift/ @github/codeql-swift
|
||||
/misc/codegen/ @github/codeql-swift
|
||||
/java/kotlin-extractor/ @github/codeql-kotlin
|
||||
/java/kotlin-explorer/ @github/codeql-kotlin
|
||||
|
||||
# ML-powered queries
|
||||
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
|
||||
@@ -39,6 +40,3 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
|
||||
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
|
||||
/.github/workflows/ruby-* @github/codeql-ruby
|
||||
/.github/workflows/swift.yml @github/codeql-swift
|
||||
|
||||
# Misc
|
||||
/misc/scripts/accept-expected-changes-from-ci.py @RasmusWL
|
||||
|
||||
@@ -14,16 +14,14 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
1. **Directory structure**
|
||||
|
||||
There are eight language-specific query directories in this repository:
|
||||
There are six language-specific query directories in this repository:
|
||||
|
||||
* C/C++: `cpp/ql/src`
|
||||
* C#: `csharp/ql/src`
|
||||
* Go: `go/ql/src`
|
||||
* Java/Kotlin: `java/ql/src`
|
||||
* Java: `java/ql/src`
|
||||
* JavaScript: `javascript/ql/src`
|
||||
* Python: `python/ql/src`
|
||||
* Ruby: `ruby/ql/src`
|
||||
* Swift: `swift/ql/src`
|
||||
|
||||
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`.
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"files": [
|
||||
"javascript/ql/lib/semmlecode.javascript.dbscheme",
|
||||
"python/ql/lib/semmlecode.python.dbscheme",
|
||||
"ruby/ql/lib/ruby.dbscheme",
|
||||
"ql/ql/src/ql.dbscheme"
|
||||
],
|
||||
"fragments": [
|
||||
"/*- External data -*/",
|
||||
"/*- Files and folders -*/",
|
||||
"/*- Diagnostic messages -*/",
|
||||
"/*- Diagnostic messages: severity -*/",
|
||||
"/*- Source location prefix -*/",
|
||||
"/*- Lines of code -*/",
|
||||
"/*- Configuration files with key value pairs -*/",
|
||||
"/*- YAML -*/",
|
||||
"/*- XML Files -*/",
|
||||
"/*- XML: sourceline -*/",
|
||||
"/*- DEPRECATED: External defects and metrics -*/",
|
||||
"/*- DEPRECATED: Snapshot date -*/",
|
||||
"/*- DEPRECATED: Duplicate code -*/",
|
||||
"/*- DEPRECATED: Version control data -*/",
|
||||
"/*- JavaScript-specific part -*/",
|
||||
"/*- Ruby dbscheme -*/",
|
||||
"/*- Erb dbscheme -*/",
|
||||
"/*- QL dbscheme -*/",
|
||||
"/*- Dbscheme dbscheme -*/",
|
||||
"/*- Yaml dbscheme -*/",
|
||||
"/*- Blame dbscheme -*/",
|
||||
"/*- JSON dbscheme -*/",
|
||||
"/*- Python dbscheme -*/"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,24 @@
|
||||
{
|
||||
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlow.qll"
|
||||
],
|
||||
"DataFlowImpl Java/C++/C#/Go/Python/Ruby/Swift": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift Legacy Configuration": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
|
||||
@@ -20,18 +40,41 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift Common": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
|
||||
],
|
||||
"TaintTracking Java/C++/C#/Go/Python/Ruby/Swift": [
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTracking.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTracking.qll"
|
||||
],
|
||||
"TaintTracking Legacy Configuration Java/C++/C#/Go/Python/Ruby/Swift": [
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
@@ -80,10 +123,6 @@
|
||||
"java/ql/src/utils/modelgenerator/internal/CaptureModels.qll",
|
||||
"csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll"
|
||||
],
|
||||
"Model as Data Generation Java/C# - CaptureModelsPrinting": [
|
||||
"java/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll",
|
||||
"csharp/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll"
|
||||
],
|
||||
"Sign Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
|
||||
@@ -470,17 +509,17 @@
|
||||
"SensitiveDataHeuristics Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
||||
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll",
|
||||
"swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll"
|
||||
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll"
|
||||
],
|
||||
"CFG": [
|
||||
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
|
||||
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll",
|
||||
"swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll"
|
||||
],
|
||||
"TypeTracker": [
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
|
||||
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
|
||||
],
|
||||
"SummaryTypeTracker": [
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
|
||||
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
|
||||
],
|
||||
"AccessPathSyntax": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
|
||||
@@ -556,9 +595,5 @@
|
||||
"EncryptionKeySizes Python/Java": [
|
||||
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
|
||||
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
|
||||
],
|
||||
"Python model summaries test extension": [
|
||||
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
|
||||
def make_groups(blocks):
|
||||
groups = {}
|
||||
for block in blocks:
|
||||
groups.setdefault("".join(block["lines"]), []).append(block)
|
||||
return list(groups.values())
|
||||
|
||||
|
||||
def validate_fragments(fragments):
|
||||
ok = True
|
||||
for header, blocks in fragments.items():
|
||||
groups = make_groups(blocks)
|
||||
if len(groups) > 1:
|
||||
ok = False
|
||||
print("Warning: dbscheme fragments with header '{}' are different for {}".format(header, ["{}:{}:{}".format(
|
||||
group[0]["file"], group[0]["start"], group[0]["end"]) for group in groups]))
|
||||
return ok
|
||||
|
||||
|
||||
def main():
|
||||
script_path = os.path.realpath(__file__)
|
||||
script_dir = os.path.dirname(script_path)
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=os.path.basename(script_path),
|
||||
description='Sync dbscheme fragments across files.'
|
||||
)
|
||||
parser.add_argument('files', metavar='dbscheme_file', type=pathlib.Path, nargs='*', default=[],
|
||||
help='dbscheme files to check')
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(os.path.join(script_dir, "dbscheme-fragments.json"), "r") as f:
|
||||
config = json.load(f)
|
||||
|
||||
fragment_headers = set(config["fragments"])
|
||||
fragments = {}
|
||||
ok = True
|
||||
for file in args.files + config["files"]:
|
||||
with open(os.path.join(os.path.dirname(script_dir), file), "r") as dbscheme:
|
||||
header = None
|
||||
line_number = 1
|
||||
block = {"file": file, "start": line_number,
|
||||
"end": None, "lines": []}
|
||||
|
||||
def end_block():
|
||||
block["end"] = line_number - 1
|
||||
if len(block["lines"]) > 0:
|
||||
if header is None:
|
||||
if re.match(r'(?m)\A(\s|//.*$|/\*(\**[^\*])*\*+/)*\Z', "".join(block["lines"])):
|
||||
# Ignore comments at the beginning of the file
|
||||
pass
|
||||
else:
|
||||
ok = False
|
||||
print("Warning: dbscheme fragment without header: {}:{}:{}".format(
|
||||
block["file"], block["start"], block["end"]))
|
||||
else:
|
||||
fragments.setdefault(header, []).append(block)
|
||||
for line in dbscheme:
|
||||
m = re.match(r"^\/\*-.*-\*\/$", line)
|
||||
if m:
|
||||
end_block()
|
||||
header = line.strip()
|
||||
if header not in fragment_headers:
|
||||
ok = False
|
||||
print("Warning: unknown header for dbscheme fragment: '{}': {}:{}".format(
|
||||
header, file, line_number))
|
||||
block = {"file": file, "start": line_number,
|
||||
"end": None, "lines": []}
|
||||
block["lines"].append(line)
|
||||
line_number += 1
|
||||
block["lines"].append('\n')
|
||||
line_number += 1
|
||||
end_block()
|
||||
if not ok or not validate_fragments(fragments):
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -327,7 +327,7 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
{
|
||||
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
|
||||
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program^ Files^ ^(x86^)\Microsoft^ Visual^ Studio^ 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
|
||||
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
|
||||
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
|
||||
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
description: Revert support for repeated initializers, which are allowed in C with designated initializers.
|
||||
compatibility: full
|
||||
aggregate_field_init.rel: reorder aggregate_field_init.rel (int aggregate, int initializer, int field, int position) aggregate initializer field
|
||||
aggregate_array_init.rel: reorder aggregate_array_init.rel (int aggregate, int initializer, int element_index, int position) aggregate initializer element_index
|
||||
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: Remove _Float128 type
|
||||
compatibility: full
|
||||
@@ -2,4 +2,3 @@ name: codeql/cpp-downgrades
|
||||
groups: cpp
|
||||
downgrades: .
|
||||
library: true
|
||||
warnOnImplicitThis: true
|
||||
|
||||
@@ -4,4 +4,3 @@ groups:
|
||||
- examples
|
||||
dependencies:
|
||||
codeql/cpp-all: ${workspace}
|
||||
warnOnImplicitThis: true
|
||||
|
||||
1
cpp/ql/examples/queries.xml
Normal file
1
cpp/ql/examples/queries.xml
Normal file
@@ -0,0 +1 @@
|
||||
<queries language="cpp"/>
|
||||
@@ -1,120 +1,3 @@
|
||||
## 0.9.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
|
||||
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
|
||||
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* The `PrintAST` library now also prints global and namespace variables and their initializers.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The library `semmle.code.cpp.dataflow.DataFlow` has been deprecated. Please use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
||||
|
||||
### New Features
|
||||
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Data flow configurations can now include a predicate `neverSkip(Node node)`
|
||||
in order to ensure inclusion of certain nodes in the path explanations. The
|
||||
predicate defaults to the end-points of the additional flow steps provided in
|
||||
the configuration, which means that such steps now always are visible by
|
||||
default in path explanations.
|
||||
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.
|
||||
|
||||
## 0.8.0
|
||||
|
||||
### New Features
|
||||
|
||||
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.
|
||||
|
||||
## 0.7.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.7.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
|
||||
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `CodeDuplication.qll` file.
|
||||
|
||||
## 0.7.2
|
||||
|
||||
### New Features
|
||||
|
||||
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.
|
||||
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `StdNamespace` class now also includes all inline namespaces that are children of `std` namespace.
|
||||
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.
|
||||
|
||||
## 0.7.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.7.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The internal `SsaConsistency` module has been moved from `SSAConstruction` to `SSAConsitency`, and the deprecated `SSAConsistency` module has been removed.
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The single-parameter predicates `ArrayOrVectorAggregateLiteral.getElementExpr` and `ClassAggregateLiteral.getFieldExpr` have been deprecated in favor of `ArrayOrVectorAggregateLiteral.getAnElementExpr` and `ClassAggregateLiteral.getAFieldExpr`.
|
||||
* The recently introduced new data flow and taint tracking APIs have had a
|
||||
number of module and predicate renamings. The old APIs remain in place for
|
||||
now.
|
||||
* The `SslContextCallAbstractConfig`, `SslContextCallConfig`, `SslContextCallBannedProtocolConfig`, `SslContextCallTls12ProtocolConfig`, `SslContextCallTls13ProtocolConfig`, `SslContextCallTlsProtocolConfig`, `SslContextFlowsToSetOptionConfig`, `SslOptionConfig` dataflow configurations from `BoostorgAsio` have been deprecated. Please use `SslContextCallConfigSig`, `SslContextCallGlobal`, `SslContextCallFlow`, `SslContextCallBannedProtocolFlow`, `SslContextCallTls12ProtocolFlow`, `SslContextCallTls13ProtocolFlow`, `SslContextCallTlsProtocolFlow`, `SslContextFlowsToSetOptionFlow`.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added overridable predicates `getSizeExpr` and `getSizeMult` to the `BufferAccess` class (`semmle.code.cpp.security.BufferAccess.qll`). This makes it possible to model a larger class of buffer reads and writes using the library.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `BufferAccess` library (`semmle.code.cpp.security.BufferAccess`) no longer matches buffer accesses inside unevaluated contexts (such as inside `sizeof` or `decltype` expressions). As a result, queries using this library may see fewer false positives.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.
|
||||
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
4
cpp/ql/lib/change-notes/2013-03-20-ssa-consistency.md
Normal file
4
cpp/ql/lib/change-notes/2013-03-20-ssa-consistency.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* The internal `SsaConsistency` module has been moved from `SSAConstruction` to `SSAConsitency`, and the deprecated `SSAConsistency` module has been removed.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `SslContextCallAbstractConfig`, `SslContextCallConfig`, `SslContextCallBannedProtocolConfig`, `SslContextCallTls12ProtocolConfig`, `SslContextCallTls13ProtocolConfig`, `SslContextCallTlsProtocolConfig`, `SslContextFlowsToSetOptionConfig`, `SslOptionConfig` dataflow configurations from `BoostorgAsio` have been deprecated. Please use `SslContextCallConfigSig`, `SslContextCallMake`, `SslContextCallFlow`, `SslContextCallBannedProtocolFlow`, `SslContextCallTls12ProtocolFlow`, `SslContextCallTls13ProtocolFlow`, `SslContextCallTlsProtocolFlow`, `SslContextFlowsToSetOptionFlow`.
|
||||
4
cpp/ql/lib/change-notes/2023-03-21-buffer-access.md
Normal file
4
cpp/ql/lib/change-notes/2023-03-21-buffer-access.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `BufferAccess` library (`semmle.code.cpp.security.BufferAccess`) no longer matches buffer accesses inside unevaluated contexts (such as inside `sizeof` or `decltype` expressions). As a result, queries using this library may see fewer false positives.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,25 +0,0 @@
|
||||
## 0.7.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The internal `SsaConsistency` module has been moved from `SSAConstruction` to `SSAConsitency`, and the deprecated `SSAConsistency` module has been removed.
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The single-parameter predicates `ArrayOrVectorAggregateLiteral.getElementExpr` and `ClassAggregateLiteral.getFieldExpr` have been deprecated in favor of `ArrayOrVectorAggregateLiteral.getAnElementExpr` and `ClassAggregateLiteral.getAFieldExpr`.
|
||||
* The recently introduced new data flow and taint tracking APIs have had a
|
||||
number of module and predicate renamings. The old APIs remain in place for
|
||||
now.
|
||||
* The `SslContextCallAbstractConfig`, `SslContextCallConfig`, `SslContextCallBannedProtocolConfig`, `SslContextCallTls12ProtocolConfig`, `SslContextCallTls13ProtocolConfig`, `SslContextCallTlsProtocolConfig`, `SslContextFlowsToSetOptionConfig`, `SslOptionConfig` dataflow configurations from `BoostorgAsio` have been deprecated. Please use `SslContextCallConfigSig`, `SslContextCallGlobal`, `SslContextCallFlow`, `SslContextCallBannedProtocolFlow`, `SslContextCallTls12ProtocolFlow`, `SslContextCallTls13ProtocolFlow`, `SslContextCallTlsProtocolFlow`, `SslContextFlowsToSetOptionFlow`.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added overridable predicates `getSizeExpr` and `getSizeMult` to the `BufferAccess` class (`semmle.code.cpp.security.BufferAccess.qll`). This makes it possible to model a larger class of buffer reads and writes using the library.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `BufferAccess` library (`semmle.code.cpp.security.BufferAccess`) no longer matches buffer accesses inside unevaluated contexts (such as inside `sizeof` or `decltype` expressions). As a result, queries using this library may see fewer false positives.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.7.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,15 +0,0 @@
|
||||
## 0.7.2
|
||||
|
||||
### New Features
|
||||
|
||||
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.
|
||||
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `StdNamespace` class now also includes all inline namespaces that are children of `std` namespace.
|
||||
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.
|
||||
@@ -1,7 +0,0 @@
|
||||
## 0.7.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
|
||||
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `CodeDuplication.qll` file.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.7.4
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,9 +0,0 @@
|
||||
## 0.8.0
|
||||
|
||||
### New Features
|
||||
|
||||
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.
|
||||
@@ -1,19 +0,0 @@
|
||||
## 0.8.1
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The library `semmle.code.cpp.dataflow.DataFlow` has been deprecated. Please use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
||||
|
||||
### New Features
|
||||
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Data flow configurations can now include a predicate `neverSkip(Node node)`
|
||||
in order to ensure inclusion of certain nodes in the path explanations. The
|
||||
predicate defaults to the end-points of the additional flow steps provided in
|
||||
the configuration, which means that such steps now always are visible by
|
||||
default in path explanations.
|
||||
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.
|
||||
@@ -1,14 +0,0 @@
|
||||
## 0.9.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
|
||||
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* The `PrintAST` library now also prints global and namespace variables and their initializers.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,5 +0,0 @@
|
||||
## 0.9.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.2
|
||||
lastReleaseVersion: 0.6.0
|
||||
|
||||
301
cpp/ql/lib/experimental/semmle/code/cpp/dataflow/ProductFlow.qll
Normal file
301
cpp/ql/lib/experimental/semmle/code/cpp/dataflow/ProductFlow.qll
Normal file
@@ -0,0 +1,301 @@
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
|
||||
module ProductFlow {
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `(source1, source2)` is a relevant data flow source.
|
||||
*
|
||||
* `source1` and `source2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSourcePair(DataFlow::Node source1, DataFlow::Node source2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `(source1, source2)` is a relevant data flow source with initial states `state1`
|
||||
* and `state2`, respectively.
|
||||
*
|
||||
* `source1` and `source2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSourcePair(
|
||||
DataFlow::Node source1, DataFlow::FlowState state1, DataFlow::Node source2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 = "" and
|
||||
state2 = "" and
|
||||
this.isSourcePair(source1, source2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(sink1, sink2)` is a relevant data flow sink.
|
||||
*
|
||||
* `sink1` and `sink2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSinkPair(DataFlow::Node sink1, DataFlow::Node sink2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `(sink1, sink2)` is a relevant data flow sink with final states `state1`
|
||||
* and `state2`, respectively.
|
||||
*
|
||||
* `sink1` and `sink2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSinkPair(
|
||||
DataFlow::Node sink1, DataFlow::FlowState state1, DataFlow::Node sink2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 = "" and
|
||||
state2 = "" and
|
||||
this.isSinkPair(sink1, sink2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited through the first projection of the product
|
||||
* dataflow graph when the flow state is `state`.
|
||||
*/
|
||||
predicate isBarrier1(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isBarrier1(node) and state = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited through the second projection of the product
|
||||
* dataflow graph when the flow state is `state`.
|
||||
*/
|
||||
predicate isBarrier2(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isBarrier2(node) and state = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited through the first projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrier1(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited through the second projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrier2(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow out of `node` is prohibited in the first projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrierOut1(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow out of `node` is prohibited in the second projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrierOut2(DataFlow::Node node) { none() }
|
||||
|
||||
/*
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
|
||||
* the first projection of the product dataflow graph.
|
||||
*/
|
||||
|
||||
predicate isAdditionalFlowStep1(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
|
||||
* the first projection of the product dataflow graph.
|
||||
*
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep1(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 instanceof DataFlow::FlowStateEmpty and
|
||||
state2 instanceof DataFlow::FlowStateEmpty and
|
||||
this.isAdditionalFlowStep1(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
|
||||
* the second projection of the product dataflow graph.
|
||||
*/
|
||||
predicate isAdditionalFlowStep2(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
|
||||
* the second projection of the product dataflow graph.
|
||||
*
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep2(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 instanceof DataFlow::FlowStateEmpty and
|
||||
state2 instanceof DataFlow::FlowStateEmpty and
|
||||
this.isAdditionalFlowStep2(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow into `node` is prohibited in the first projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrierIn1(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow into `node` is prohibited in the second projection of the product
|
||||
* dataflow graph.
|
||||
*/
|
||||
predicate isBarrierIn2(DataFlow::Node node) { none() }
|
||||
|
||||
predicate hasFlowPath(
|
||||
DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1,
|
||||
DataFlow2::PathNode sink2
|
||||
) {
|
||||
reachable(this, source1, source2, sink1, sink2)
|
||||
}
|
||||
}
|
||||
|
||||
private import Internal
|
||||
|
||||
module Internal {
|
||||
class Conf1 extends DataFlow::Configuration {
|
||||
Conf1() { this = "Conf1" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
|
||||
exists(Configuration conf | conf.isSourcePair(source, state, _, _))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
|
||||
exists(Configuration conf | conf.isSinkPair(sink, state, _, _))
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
exists(Configuration conf | conf.isBarrier1(node, state))
|
||||
}
|
||||
|
||||
override predicate isBarrierOut(DataFlow::Node node) {
|
||||
exists(Configuration conf | conf.isBarrierOut1(node))
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
exists(Configuration conf | conf.isAdditionalFlowStep1(node1, state1, node2, state2))
|
||||
}
|
||||
|
||||
override predicate isBarrierIn(DataFlow::Node node) {
|
||||
exists(Configuration conf | conf.isBarrierIn1(node))
|
||||
}
|
||||
}
|
||||
|
||||
class Conf2 extends DataFlow2::Configuration {
|
||||
Conf2() { this = "Conf2" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
|
||||
exists(Configuration conf, DataFlow::PathNode source1 |
|
||||
conf.isSourcePair(source1.getNode(), source1.getState(), source, state) and
|
||||
any(Conf1 c).hasFlowPath(source1, _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
|
||||
exists(Configuration conf, DataFlow::PathNode sink1 |
|
||||
conf.isSinkPair(sink1.getNode(), sink1.getState(), sink, state) and
|
||||
any(Conf1 c).hasFlowPath(_, sink1)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
exists(Configuration conf | conf.isBarrier2(node, state))
|
||||
}
|
||||
|
||||
override predicate isBarrierOut(DataFlow::Node node) {
|
||||
exists(Configuration conf | conf.isBarrierOut2(node))
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
exists(Configuration conf | conf.isAdditionalFlowStep2(node1, state1, node2, state2))
|
||||
}
|
||||
|
||||
override predicate isBarrierIn(DataFlow::Node node) {
|
||||
exists(Configuration conf | conf.isBarrierIn2(node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachableInterprocEntry(
|
||||
Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
|
||||
DataFlow::PathNode node1, DataFlow2::PathNode node2
|
||||
) {
|
||||
conf.isSourcePair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState()) and
|
||||
node1 = source1 and
|
||||
node2 = source2
|
||||
or
|
||||
exists(
|
||||
DataFlow::PathNode midEntry1, DataFlow2::PathNode midEntry2, DataFlow::PathNode midExit1,
|
||||
DataFlow2::PathNode midExit2
|
||||
|
|
||||
reachableInterprocEntry(conf, source1, source2, midEntry1, midEntry2) and
|
||||
interprocEdgePair(midExit1, midExit2, node1, node2) and
|
||||
localPathStep1*(midEntry1, midExit1) and
|
||||
localPathStep2*(midEntry2, midExit2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate localPathStep1(DataFlow::PathNode pred, DataFlow::PathNode succ) {
|
||||
DataFlow::PathGraph::edges(pred, succ) and
|
||||
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
|
||||
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
|
||||
}
|
||||
|
||||
private predicate localPathStep2(DataFlow2::PathNode pred, DataFlow2::PathNode succ) {
|
||||
DataFlow2::PathGraph::edges(pred, succ) and
|
||||
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
|
||||
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate interprocEdge1(
|
||||
Declaration predDecl, Declaration succDecl, DataFlow::PathNode pred1, DataFlow::PathNode succ1
|
||||
) {
|
||||
DataFlow::PathGraph::edges(pred1, succ1) and
|
||||
predDecl != succDecl and
|
||||
pred1.getNode().getEnclosingCallable() = predDecl and
|
||||
succ1.getNode().getEnclosingCallable() = succDecl
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate interprocEdge2(
|
||||
Declaration predDecl, Declaration succDecl, DataFlow2::PathNode pred2, DataFlow2::PathNode succ2
|
||||
) {
|
||||
DataFlow2::PathGraph::edges(pred2, succ2) and
|
||||
predDecl != succDecl and
|
||||
pred2.getNode().getEnclosingCallable() = predDecl and
|
||||
succ2.getNode().getEnclosingCallable() = succDecl
|
||||
}
|
||||
|
||||
private predicate interprocEdgePair(
|
||||
DataFlow::PathNode pred1, DataFlow2::PathNode pred2, DataFlow::PathNode succ1,
|
||||
DataFlow2::PathNode succ2
|
||||
) {
|
||||
exists(Declaration predDecl, Declaration succDecl |
|
||||
interprocEdge1(predDecl, succDecl, pred1, succ1) and
|
||||
interprocEdge2(predDecl, succDecl, pred2, succ2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reachable(
|
||||
Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
|
||||
DataFlow::PathNode sink1, DataFlow2::PathNode sink2
|
||||
) {
|
||||
exists(DataFlow::PathNode mid1, DataFlow2::PathNode mid2 |
|
||||
reachableInterprocEntry(conf, source1, source2, mid1, mid2) and
|
||||
conf.isSinkPair(sink1.getNode(), sink1.getState(), sink2.getNode(), sink2.getState()) and
|
||||
localPathStep1*(mid1, sink1) and
|
||||
localPathStep2*(mid2, sink2)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1 +1,86 @@
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.Bound
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
private newtype TBound =
|
||||
TBoundZero() or
|
||||
TBoundValueNumber(ValueNumber vn) {
|
||||
exists(Instruction i |
|
||||
vn.getAnInstruction() = i and
|
||||
(
|
||||
i.getResultIRType() instanceof IRIntegerType or
|
||||
i.getResultIRType() instanceof IRAddressType
|
||||
) and
|
||||
not vn.getAnInstruction() instanceof ConstantInstruction
|
||||
|
|
||||
i instanceof PhiInstruction
|
||||
or
|
||||
i instanceof InitializeParameterInstruction
|
||||
or
|
||||
i instanceof CallInstruction
|
||||
or
|
||||
i instanceof VariableAddressInstruction
|
||||
or
|
||||
i instanceof FieldAddressInstruction
|
||||
or
|
||||
i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction
|
||||
or
|
||||
i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction
|
||||
or
|
||||
i.getAUse() instanceof ArgumentOperand
|
||||
or
|
||||
i instanceof PointerArithmeticInstruction
|
||||
or
|
||||
i.getAUse() instanceof AddressOperand
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound that may be inferred for an expression plus/minus an integer delta.
|
||||
*/
|
||||
abstract class Bound extends TBound {
|
||||
abstract string toString();
|
||||
|
||||
/** Gets an expression that equals this bound plus `delta`. */
|
||||
abstract Instruction getInstruction(int delta);
|
||||
|
||||
/** Gets an expression that equals this bound. */
|
||||
Instruction getInstruction() { result = getInstruction(0) }
|
||||
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* The bound that corresponds to the integer 0. This is used to represent all
|
||||
* integer bounds as bounds are always accompanied by an added integer delta.
|
||||
*/
|
||||
class ZeroBound extends Bound, TBoundZero {
|
||||
override string toString() { result = "0" }
|
||||
|
||||
override Instruction getInstruction(int delta) {
|
||||
result.(ConstantValueInstruction).getValue().toInt() = delta
|
||||
}
|
||||
|
||||
override Location getLocation() { result instanceof UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound corresponding to the value of an `Instruction`.
|
||||
*/
|
||||
class ValueNumberBound extends Bound, TBoundValueNumber {
|
||||
ValueNumber vn;
|
||||
|
||||
ValueNumberBound() { this = TBoundValueNumber(vn) }
|
||||
|
||||
/** Gets an `Instruction` that equals this bound. */
|
||||
override Instruction getInstruction(int delta) {
|
||||
this = TBoundValueNumber(valueNumber(result)) and delta = 0
|
||||
}
|
||||
|
||||
override string toString() { result = "ValueNumberBound" }
|
||||
|
||||
override Location getLocation() { result = vn.getLocation() }
|
||||
|
||||
/** Gets the value number that equals this bound. */
|
||||
ValueNumber getValueNumber() { result = vn }
|
||||
}
|
||||
|
||||
@@ -3,4 +3,3 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
// Import each extension we want to enable
|
||||
import extensions.SubtractSelf
|
||||
import extensions.ConstantBitwiseAndExprRange
|
||||
import extensions.StrlenLiteralRangeExpr
|
||||
|
||||
@@ -238,7 +238,7 @@ class NoReason extends Reason, TNoReason {
|
||||
class CondReason extends Reason, TCondReason {
|
||||
IRGuardCondition getCond() { this = TCondReason(result) }
|
||||
|
||||
override string toString() { result = this.getCond().toString() }
|
||||
override string toString() { result = getCond().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,14 +260,14 @@ private predicate typeBound(IRIntegerType typ, int lowerbound, int upperbound) {
|
||||
private class NarrowingCastInstruction extends ConvertInstruction {
|
||||
NarrowingCastInstruction() {
|
||||
not this instanceof SafeCastInstruction and
|
||||
typeBound(this.getResultIRType(), _, _)
|
||||
typeBound(getResultIRType(), _, _)
|
||||
}
|
||||
|
||||
/** Gets the lower bound of the resulting type. */
|
||||
int getLowerBound() { typeBound(this.getResultIRType(), result, _) }
|
||||
int getLowerBound() { typeBound(getResultIRType(), result, _) }
|
||||
|
||||
/** Gets the upper bound of the resulting type. */
|
||||
int getUpperBound() { typeBound(this.getResultIRType(), _, result) }
|
||||
int getUpperBound() { typeBound(getResultIRType(), _, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -109,8 +109,8 @@ private predicate safeCast(IRIntegerType fromtyp, IRIntegerType totyp) {
|
||||
*/
|
||||
class PtrToPtrCastInstruction extends ConvertInstruction {
|
||||
PtrToPtrCastInstruction() {
|
||||
this.getResultIRType() instanceof IRAddressType and
|
||||
this.getUnary().getResultIRType() instanceof IRAddressType
|
||||
getResultIRType() instanceof IRAddressType and
|
||||
getUnary().getResultIRType() instanceof IRAddressType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class PtrToPtrCastInstruction extends ConvertInstruction {
|
||||
* that cannot overflow or underflow.
|
||||
*/
|
||||
class SafeIntCastInstruction extends ConvertInstruction {
|
||||
SafeIntCastInstruction() { safeCast(this.getUnary().getResultIRType(), this.getResultIRType()) }
|
||||
SafeIntCastInstruction() { safeCast(getUnary().getResultIRType(), getResultIRType()) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,8 +50,8 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
|
||||
// If an operand can have negative values, the lower bound is unconstrained.
|
||||
// Otherwise, the lower bound is zero.
|
||||
exists(float lLower, float rLower |
|
||||
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
(
|
||||
(lLower < 0 or rLower < 0) and
|
||||
result = exprMinVal(this)
|
||||
@@ -68,10 +68,10 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
|
||||
// If an operand can have negative values, the upper bound is unconstrained.
|
||||
// Otherwise, the upper bound is the minimum of the upper bounds of the operands
|
||||
exists(float lLower, float lUpper, float rLower, float rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
(
|
||||
(lLower < 0 or rLower < 0) and
|
||||
result = exprMaxVal(this)
|
||||
@@ -85,6 +85,6 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
|
||||
}
|
||||
|
||||
override predicate dependsOnChild(Expr child) {
|
||||
child = this.getLeftOperand() or child = this.getRightOperand()
|
||||
child = getLeftOperand() or child = getRightOperand()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
* We don't handle the case where `a` and `b` are both non-constant values.
|
||||
*/
|
||||
ConstantRShiftExprRange() {
|
||||
this.getUnspecifiedType() instanceof IntegralType and
|
||||
getUnspecifiedType() instanceof IntegralType and
|
||||
exists(Expr l, Expr r |
|
||||
l = this.(RShiftExpr).getLeftOperand() and
|
||||
r = this.(RShiftExpr).getRightOperand()
|
||||
@@ -84,10 +84,10 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
|
||||
override float getLowerBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
@@ -95,8 +95,8 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, this.getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, this.getLeftOperand())
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
@@ -111,10 +111,10 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
|
||||
override float getUpperBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
@@ -122,8 +122,8 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, this.getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, this.getLeftOperand())
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
@@ -137,7 +137,7 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
}
|
||||
|
||||
override predicate dependsOnChild(Expr child) {
|
||||
child = this.getLeftOperand() or child = this.getRightOperand()
|
||||
child = getLeftOperand() or child = getRightOperand()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
* We don't handle the case where `a` and `b` are both non-constant values.
|
||||
*/
|
||||
ConstantLShiftExprRange() {
|
||||
this.getUnspecifiedType() instanceof IntegralType and
|
||||
getUnspecifiedType() instanceof IntegralType and
|
||||
exists(Expr l, Expr r |
|
||||
l = this.(LShiftExpr).getLeftOperand() and
|
||||
r = this.(LShiftExpr).getRightOperand()
|
||||
@@ -197,10 +197,10 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
|
||||
override float getLowerBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
@@ -208,8 +208,8 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, this.getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, this.getLeftOperand())
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
@@ -228,10 +228,10 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
|
||||
override float getUpperBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
@@ -239,8 +239,8 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, this.getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, this.getLeftOperand())
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
@@ -258,6 +258,6 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
}
|
||||
|
||||
override predicate dependsOnChild(Expr child) {
|
||||
child = this.getLeftOperand() or child = this.getRightOperand()
|
||||
child = getLeftOperand() or child = getRightOperand()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
/**
|
||||
* This module implements subclasses for various DataFlow nodes that extends
|
||||
* their `toString()` predicates with range information, if applicable. By
|
||||
* including this module in a `path-problem` query, this range information
|
||||
* will be displayed at each step in the query results.
|
||||
*
|
||||
* This is currently implemented for `DataFlow::ExprNode` and `DataFlow::DefinitionByReferenceNode`,
|
||||
* but it is not yet implemented for `DataFlow::ParameterNode`.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.DataFlow
|
||||
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
string getExprBoundAsString(Expr e) {
|
||||
if exists(lowerBound(e)) and exists(upperBound(e))
|
||||
then result = "[" + lowerBound(e) + ", " + upperBound(e) + "]"
|
||||
else result = "[unknown range]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for any integer type after resolving typedefs and stripping `const`
|
||||
* specifiers, such as for `const size_t`
|
||||
*/
|
||||
predicate isIntegralType(Type t) {
|
||||
// We use `getUnspecifiedType` here because without it things like
|
||||
// `const size_t` aren't considered to be integral
|
||||
t.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for any reference to an integer type after resolving typedefs and
|
||||
* stripping `const` specifiers, such as for `const size_t&`
|
||||
*/
|
||||
predicate isIntegralReferenceType(Type t) { isIntegralType(t.(ReferenceType).stripType()) }
|
||||
|
||||
/**
|
||||
* Holds for any pointer to an integer type after resolving typedefs and
|
||||
* stripping `const` specifiers, such as for `const size_t*`. This predicate
|
||||
* holds for any pointer depth, such as for `const size_t**`.
|
||||
*/
|
||||
predicate isIntegralPointerType(Type t) { isIntegralType(t.(PointerType).stripType()) }
|
||||
|
||||
predicate hasIntegralOrReferenceIntegralType(Locatable e) {
|
||||
exists(Type t |
|
||||
(
|
||||
t = e.(Expr).getUnspecifiedType()
|
||||
or
|
||||
// This will cover variables, parameters, type declarations, etc.
|
||||
t = e.(DeclarationEntry).getUnspecifiedType()
|
||||
) and
|
||||
(isIntegralType(t) or isIntegralReferenceType(t))
|
||||
)
|
||||
}
|
||||
|
||||
Expr getLOp(Operation o) {
|
||||
result = o.(BinaryOperation).getLeftOperand() or
|
||||
result = o.(Assignment).getLValue()
|
||||
}
|
||||
|
||||
Expr getROp(Operation o) {
|
||||
result = o.(BinaryOperation).getRightOperand() or
|
||||
result = o.(Assignment).getRValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the ranges of expressions in the path view
|
||||
*/
|
||||
private class ExprRangeNode extends DataFlow::ExprNode {
|
||||
pragma[inline]
|
||||
private string getIntegralBounds(Expr arg) {
|
||||
if hasIntegralOrReferenceIntegralType(arg)
|
||||
then result = getExprBoundAsString(arg)
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string getOperationBounds(Operation e) {
|
||||
result =
|
||||
getExprBoundAsString(e) + " = " + getExprBoundAsString(getLOp(e)) + e.getOperator() +
|
||||
getExprBoundAsString(getROp(e))
|
||||
}
|
||||
|
||||
private string getCallBounds(Call e) {
|
||||
result =
|
||||
getExprBoundAsString(e) + "(" +
|
||||
concat(Expr arg, int i |
|
||||
arg = e.getArgument(i)
|
||||
|
|
||||
this.getIntegralBounds(arg), "," order by i
|
||||
) + ")"
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
exists(Expr e | e = this.getExpr() |
|
||||
if hasIntegralOrReferenceIntegralType(e)
|
||||
then
|
||||
result = super.toString() + ": " + this.getOperationBounds(e)
|
||||
or
|
||||
result = super.toString() + ": " + this.getCallBounds(e)
|
||||
or
|
||||
not exists(this.getOperationBounds(e)) and
|
||||
not exists(this.getCallBounds(e)) and
|
||||
result = super.toString() + ": " + getExprBoundAsString(e)
|
||||
else result = super.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the ranges of expressions in the path view
|
||||
*/
|
||||
private class ReferenceArgumentRangeNode extends DataFlow::DefinitionByReferenceNode {
|
||||
override string toString() {
|
||||
if hasIntegralOrReferenceIntegralType(this.asDefiningArgument())
|
||||
then result = super.toString() + ": " + getExprBoundAsString(this.getArgument())
|
||||
else result = super.toString()
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
private import cpp
|
||||
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
|
||||
|
||||
/**
|
||||
* Provides range analysis information for calls to `strlen` on literal strings.
|
||||
* For example, the range of `strlen("literal")` will be 7.
|
||||
*/
|
||||
class StrlenLiteralRangeExpr extends SimpleRangeAnalysisExpr, FunctionCall {
|
||||
StrlenLiteralRangeExpr() {
|
||||
this.getTarget().hasGlobalOrStdName("strlen") and this.getArgument(0).isConstant()
|
||||
}
|
||||
|
||||
override int getLowerBounds() { result = this.getArgument(0).getValue().length() }
|
||||
|
||||
override int getUpperBounds() { result = this.getArgument(0).getValue().length() }
|
||||
|
||||
override predicate dependsOnChild(Expr e) { none() }
|
||||
}
|
||||
@@ -3,8 +3,8 @@ import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
|
||||
private class SelfSub extends SimpleRangeAnalysisExpr, SubExpr {
|
||||
SelfSub() {
|
||||
// Match `x - x` but not `myInt - (unsigned char)myInt`.
|
||||
this.getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() =
|
||||
this.getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget()
|
||||
getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() =
|
||||
getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget()
|
||||
}
|
||||
|
||||
override float getLowerBounds() { result = 0 }
|
||||
|
||||
@@ -54,7 +54,7 @@ module PrivateCleartextWrite {
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
module WriteFlow = TaintTracking::Global<WriteConfig>;
|
||||
module WriteFlow = TaintTracking::Make<WriteConfig>;
|
||||
|
||||
class PrivateDataSource extends Source {
|
||||
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
|
||||
|
||||
@@ -87,7 +87,7 @@ class SemIntegerLiteralExpr extends SemNumericLiteralExpr {
|
||||
final int getIntValue() { Specific::integerLiteral(this, _, result) }
|
||||
|
||||
final override float getApproximateFloatValue() {
|
||||
result = this.getIntValue()
|
||||
result = getIntValue()
|
||||
or
|
||||
Specific::largeIntegerLiteral(this, _, result)
|
||||
}
|
||||
@@ -124,13 +124,13 @@ class SemBinaryExpr extends SemKnownExpr {
|
||||
|
||||
/** Holds if `a` and `b` are the two operands, in either order. */
|
||||
final predicate hasOperands(SemExpr a, SemExpr b) {
|
||||
a = this.getLeftOperand() and b = this.getRightOperand()
|
||||
a = getLeftOperand() and b = getRightOperand()
|
||||
or
|
||||
a = this.getRightOperand() and b = this.getLeftOperand()
|
||||
a = getRightOperand() and b = getLeftOperand()
|
||||
}
|
||||
|
||||
/** Gets the two operands. */
|
||||
final SemExpr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() }
|
||||
final SemExpr getAnOperand() { result = getLeftOperand() or result = getRightOperand() }
|
||||
}
|
||||
|
||||
/** An expression that performs and ordered comparison of two operands. */
|
||||
@@ -154,8 +154,8 @@ class SemRelationalExpr extends SemBinaryExpr {
|
||||
*/
|
||||
final SemExpr getLesserOperand() {
|
||||
if opcode instanceof Opcode::CompareLT or opcode instanceof Opcode::CompareLE
|
||||
then result = this.getLeftOperand()
|
||||
else result = this.getRightOperand()
|
||||
then result = getLeftOperand()
|
||||
else result = getRightOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,8 +167,8 @@ class SemRelationalExpr extends SemBinaryExpr {
|
||||
*/
|
||||
final SemExpr getGreaterOperand() {
|
||||
if opcode instanceof Opcode::CompareGT or opcode instanceof Opcode::CompareGE
|
||||
then result = this.getLeftOperand()
|
||||
else result = this.getRightOperand()
|
||||
then result = getLeftOperand()
|
||||
else result = getRightOperand()
|
||||
}
|
||||
|
||||
/** Holds if this comparison returns `false` if the two operands are equal. */
|
||||
@@ -280,11 +280,11 @@ class SemLoadExpr extends SemNullaryExpr {
|
||||
}
|
||||
|
||||
class SemSsaLoadExpr extends SemLoadExpr {
|
||||
SemSsaLoadExpr() { exists(this.getDef()) }
|
||||
SemSsaLoadExpr() { exists(getDef()) }
|
||||
}
|
||||
|
||||
class SemNonSsaLoadExpr extends SemLoadExpr {
|
||||
SemNonSsaLoadExpr() { not exists(this.getDef()) }
|
||||
SemNonSsaLoadExpr() { not exists(getDef()) }
|
||||
}
|
||||
|
||||
class SemStoreExpr extends SemUnaryExpr {
|
||||
@@ -0,0 +1,356 @@
|
||||
/**
|
||||
* C++-specific implementation of the semantic interface.
|
||||
*/
|
||||
|
||||
private import cpp as Cpp
|
||||
private import semmle.code.cpp.ir.IR as IR
|
||||
private import Semantic
|
||||
private import experimental.semmle.code.cpp.rangeanalysis.Bound as IRBound
|
||||
private import semmle.code.cpp.controlflow.IRGuards as IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
module SemanticExprConfig {
|
||||
class Location = Cpp::Location;
|
||||
|
||||
class Expr = IR::Instruction;
|
||||
|
||||
SemBasicBlock getExprBasicBlock(Expr e) { result = getSemanticBasicBlock(e.getBlock()) }
|
||||
|
||||
private predicate anyConstantExpr(Expr expr, SemType type, string value) {
|
||||
exists(IR::ConstantInstruction instr | instr = expr |
|
||||
type = getSemanticType(instr.getResultIRType()) and
|
||||
value = instr.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
predicate integerLiteral(Expr expr, SemIntegerType type, int value) {
|
||||
exists(string valueString |
|
||||
anyConstantExpr(expr, type, valueString) and
|
||||
value = valueString.toInt()
|
||||
)
|
||||
}
|
||||
|
||||
predicate largeIntegerLiteral(Expr expr, SemIntegerType type, float approximateFloatValue) {
|
||||
exists(string valueString |
|
||||
anyConstantExpr(expr, type, valueString) and
|
||||
not exists(valueString.toInt()) and
|
||||
approximateFloatValue = valueString.toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
predicate floatingPointLiteral(Expr expr, SemFloatingPointType type, float value) {
|
||||
exists(string valueString |
|
||||
anyConstantExpr(expr, type, valueString) and value = valueString.toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
predicate booleanLiteral(Expr expr, SemBooleanType type, boolean value) {
|
||||
exists(string valueString |
|
||||
anyConstantExpr(expr, type, valueString) and
|
||||
(
|
||||
valueString = "true" and value = true
|
||||
or
|
||||
valueString = "false" and value = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate nullLiteral(Expr expr, SemAddressType type) { anyConstantExpr(expr, type, _) }
|
||||
|
||||
predicate stringLiteral(Expr expr, SemType type, string value) {
|
||||
anyConstantExpr(expr, type, value) and expr instanceof IR::StringConstantInstruction
|
||||
}
|
||||
|
||||
predicate binaryExpr(Expr expr, Opcode opcode, SemType type, Expr leftOperand, Expr rightOperand) {
|
||||
exists(IR::BinaryInstruction instr | instr = expr |
|
||||
type = getSemanticType(instr.getResultIRType()) and
|
||||
leftOperand = instr.getLeft() and
|
||||
rightOperand = instr.getRight() and
|
||||
// REVIEW: Merge the two `Opcode` types.
|
||||
opcode.toString() = instr.getOpcode().toString()
|
||||
)
|
||||
}
|
||||
|
||||
predicate unaryExpr(Expr expr, Opcode opcode, SemType type, Expr operand) {
|
||||
type = getSemanticType(expr.getResultIRType()) and
|
||||
(
|
||||
exists(IR::UnaryInstruction instr | instr = expr |
|
||||
operand = instr.getUnary() and
|
||||
// REVIEW: Merge the two operand types.
|
||||
opcode.toString() = instr.getOpcode().toString()
|
||||
)
|
||||
or
|
||||
exists(IR::StoreInstruction instr | instr = expr |
|
||||
operand = instr.getSourceValue() and
|
||||
opcode instanceof Opcode::Store
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate nullaryExpr(Expr expr, Opcode opcode, SemType type) {
|
||||
type = getSemanticType(expr.getResultIRType()) and
|
||||
(
|
||||
expr instanceof IR::LoadInstruction and opcode instanceof Opcode::Load
|
||||
or
|
||||
expr instanceof IR::InitializeParameterInstruction and
|
||||
opcode instanceof Opcode::InitializeParameter
|
||||
)
|
||||
}
|
||||
|
||||
predicate conditionalExpr(
|
||||
Expr expr, SemType type, Expr condition, Expr trueResult, Expr falseResult
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
SemType getUnknownExprType(Expr expr) { result = getSemanticType(expr.getResultIRType()) }
|
||||
|
||||
class BasicBlock = IR::IRBlock;
|
||||
|
||||
predicate bbDominates(BasicBlock dominator, BasicBlock dominated) {
|
||||
dominator.dominates(dominated)
|
||||
}
|
||||
|
||||
predicate hasDominanceInformation(BasicBlock block) { any() }
|
||||
|
||||
private predicate id(Cpp::Locatable x, Cpp::Locatable y) { x = y }
|
||||
|
||||
private predicate idOf(Cpp::Locatable x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
|
||||
int getBasicBlockUniqueId(BasicBlock block) { idOf(block.getFirstInstruction().getAst(), result) }
|
||||
|
||||
newtype TSsaVariable =
|
||||
TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or
|
||||
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() } or
|
||||
TSsaPointerArithmeticGuard(IR::PointerArithmeticInstruction instr) {
|
||||
exists(Guard g, IR::Operand use | use = instr.getAUse() |
|
||||
g.comparesLt(use, _, _, _, _) or
|
||||
g.comparesLt(_, use, _, _, _) or
|
||||
g.comparesEq(use, _, _, _, _) or
|
||||
g.comparesEq(_, use, _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
class SsaVariable extends TSsaVariable {
|
||||
string toString() { none() }
|
||||
|
||||
Location getLocation() { none() }
|
||||
|
||||
IR::Instruction asInstruction() { none() }
|
||||
|
||||
IR::PointerArithmeticInstruction asPointerArithGuard() { none() }
|
||||
|
||||
IR::Operand asOperand() { none() }
|
||||
}
|
||||
|
||||
class SsaInstructionVariable extends SsaVariable, TSsaInstruction {
|
||||
IR::Instruction instr;
|
||||
|
||||
SsaInstructionVariable() { this = TSsaInstruction(instr) }
|
||||
|
||||
final override string toString() { result = instr.toString() }
|
||||
|
||||
final override Location getLocation() { result = instr.getLocation() }
|
||||
|
||||
final override IR::Instruction asInstruction() { result = instr }
|
||||
}
|
||||
|
||||
class SsaPointerArithmeticGuard extends SsaVariable, TSsaPointerArithmeticGuard {
|
||||
IR::PointerArithmeticInstruction instr;
|
||||
|
||||
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(instr) }
|
||||
|
||||
final override string toString() { result = instr.toString() }
|
||||
|
||||
final override Location getLocation() { result = instr.getLocation() }
|
||||
|
||||
final override IR::PointerArithmeticInstruction asPointerArithGuard() { result = instr }
|
||||
}
|
||||
|
||||
class SsaOperand extends SsaVariable, TSsaOperand {
|
||||
IR::Operand op;
|
||||
|
||||
SsaOperand() { this = TSsaOperand(op) }
|
||||
|
||||
final override string toString() { result = op.toString() }
|
||||
|
||||
final override Location getLocation() { result = op.getLocation() }
|
||||
|
||||
final override IR::Operand asOperand() { result = op }
|
||||
}
|
||||
|
||||
predicate explicitUpdate(SsaVariable v, Expr sourceExpr) { v.asInstruction() = sourceExpr }
|
||||
|
||||
predicate phi(SsaVariable v) { v.asInstruction() instanceof IR::PhiInstruction }
|
||||
|
||||
SsaVariable getAPhiInput(SsaVariable v) {
|
||||
exists(IR::PhiInstruction instr | v.asInstruction() = instr |
|
||||
result.asInstruction() = instr.getAnInput()
|
||||
or
|
||||
result.asOperand() = instr.getAnInputOperand()
|
||||
)
|
||||
}
|
||||
|
||||
Expr getAUse(SsaVariable v) {
|
||||
result.(IR::LoadInstruction).getSourceValue() = v.asInstruction()
|
||||
or
|
||||
result = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
||||
}
|
||||
|
||||
SemType getSsaVariableType(SsaVariable v) {
|
||||
result = getSemanticType(v.asInstruction().getResultIRType())
|
||||
}
|
||||
|
||||
BasicBlock getSsaVariableBasicBlock(SsaVariable v) {
|
||||
result = v.asInstruction().getBlock()
|
||||
or
|
||||
result = v.asOperand().getUse().getBlock()
|
||||
}
|
||||
|
||||
private newtype TReadPosition =
|
||||
TReadPositionBlock(IR::IRBlock block) or
|
||||
TReadPositionPhiInputEdge(IR::IRBlock pred, IR::IRBlock succ) {
|
||||
exists(IR::PhiInputOperand input |
|
||||
pred = input.getPredecessorBlock() and
|
||||
succ = input.getUse().getBlock()
|
||||
)
|
||||
}
|
||||
|
||||
class SsaReadPosition extends TReadPosition {
|
||||
string toString() { none() }
|
||||
|
||||
Location getLocation() { none() }
|
||||
|
||||
predicate hasRead(SsaVariable v) { none() }
|
||||
}
|
||||
|
||||
private class SsaReadPositionBlock extends SsaReadPosition, TReadPositionBlock {
|
||||
IR::IRBlock block;
|
||||
|
||||
SsaReadPositionBlock() { this = TReadPositionBlock(block) }
|
||||
|
||||
final override string toString() { result = block.toString() }
|
||||
|
||||
final override Location getLocation() { result = block.getLocation() }
|
||||
|
||||
final override predicate hasRead(SsaVariable v) {
|
||||
exists(IR::Operand operand |
|
||||
operand.getDef() = v.asInstruction() or
|
||||
operand.getDef() = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
||||
|
|
||||
not operand instanceof IR::PhiInputOperand and
|
||||
operand.getUse().getBlock() = block
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SsaReadPositionPhiInputEdge extends SsaReadPosition, TReadPositionPhiInputEdge {
|
||||
IR::IRBlock pred;
|
||||
IR::IRBlock succ;
|
||||
|
||||
SsaReadPositionPhiInputEdge() { this = TReadPositionPhiInputEdge(pred, succ) }
|
||||
|
||||
final override string toString() { result = pred.toString() + "->" + succ.toString() }
|
||||
|
||||
final override Location getLocation() { result = succ.getLocation() }
|
||||
|
||||
final override predicate hasRead(SsaVariable v) {
|
||||
exists(IR::PhiInputOperand operand |
|
||||
operand.getDef() = v.asInstruction() or
|
||||
operand.getDef() = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
||||
|
|
||||
operand.getPredecessorBlock() = pred and
|
||||
operand.getUse().getBlock() = succ
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate hasReadOfSsaVariable(SsaReadPosition pos, SsaVariable v) { pos.hasRead(v) }
|
||||
|
||||
predicate readBlock(SsaReadPosition pos, BasicBlock block) { pos = TReadPositionBlock(block) }
|
||||
|
||||
predicate phiInputEdge(SsaReadPosition pos, BasicBlock origBlock, BasicBlock phiBlock) {
|
||||
pos = TReadPositionPhiInputEdge(origBlock, phiBlock)
|
||||
}
|
||||
|
||||
predicate phiInput(SsaReadPosition pos, SsaVariable phi, SsaVariable input) {
|
||||
exists(IR::PhiInputOperand operand |
|
||||
pos = TReadPositionPhiInputEdge(operand.getPredecessorBlock(), operand.getUse().getBlock())
|
||||
|
|
||||
phi.asInstruction() = operand.getUse() and
|
||||
(
|
||||
input.asInstruction() = operand.getDef()
|
||||
or
|
||||
input.asOperand() = operand
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class Bound instanceof IRBound::Bound {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
final Location getLocation() { result = super.getLocation() }
|
||||
}
|
||||
|
||||
private class ValueNumberBound extends Bound instanceof IRBound::ValueNumberBound {
|
||||
override string toString() { result = IRBound::ValueNumberBound.super.toString() }
|
||||
}
|
||||
|
||||
predicate zeroBound(Bound bound) { bound instanceof IRBound::ZeroBound }
|
||||
|
||||
predicate ssaBound(Bound bound, SsaVariable v) {
|
||||
v.asInstruction() = bound.(IRBound::ValueNumberBound).getValueNumber().getAnInstruction()
|
||||
}
|
||||
|
||||
Expr getBoundExpr(Bound bound, int delta) {
|
||||
result = bound.(IRBound::Bound).getInstruction(delta)
|
||||
}
|
||||
|
||||
class Guard = IRGuards::IRGuardCondition;
|
||||
|
||||
predicate guard(Guard guard, BasicBlock block) { block = guard.getBlock() }
|
||||
|
||||
Expr getGuardAsExpr(Guard guard) { result = guard }
|
||||
|
||||
predicate equalityGuard(Guard guard, Expr e1, Expr e2, boolean polarity) {
|
||||
guard.comparesEq(e1.getAUse(), e2.getAUse(), 0, true, polarity)
|
||||
}
|
||||
|
||||
predicate guardDirectlyControlsBlock(Guard guard, BasicBlock controlled, boolean branch) {
|
||||
guard.controls(controlled, branch)
|
||||
}
|
||||
|
||||
predicate guardHasBranchEdge(Guard guard, BasicBlock bb1, BasicBlock bb2, boolean branch) {
|
||||
guard.controlsEdge(bb1, bb2, branch)
|
||||
}
|
||||
|
||||
Guard comparisonGuard(Expr e) { result = e }
|
||||
|
||||
predicate implies_v2(Guard g1, boolean b1, Guard g2, boolean b2) {
|
||||
none() // TODO
|
||||
}
|
||||
}
|
||||
|
||||
SemExpr getSemanticExpr(IR::Instruction instr) { result = instr }
|
||||
|
||||
IR::Instruction getCppInstruction(SemExpr e) { e = result }
|
||||
|
||||
SemBasicBlock getSemanticBasicBlock(IR::IRBlock block) { result = block }
|
||||
|
||||
IR::IRBlock getCppBasicBlock(SemBasicBlock block) { block = result }
|
||||
|
||||
SemSsaVariable getSemanticSsaVariable(IR::Instruction instr) {
|
||||
result.(SemanticExprConfig::SsaVariable).asInstruction() = instr
|
||||
}
|
||||
|
||||
IR::Instruction getCppSsaVariableInstruction(SemSsaVariable var) {
|
||||
var.(SemanticExprConfig::SsaVariable).asInstruction() = result
|
||||
}
|
||||
|
||||
SemBound getSemanticBound(IRBound::Bound bound) { result = bound }
|
||||
|
||||
IRBound::Bound getCppBound(SemBound bound) { bound = result }
|
||||
|
||||
SemGuard getSemanticGuard(IRGuards::IRGuardCondition guard) { result = guard }
|
||||
|
||||
IRGuards::IRGuardCondition getCppGuard(SemGuard guard) { guard = result }
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Semantic interface to the SSA library.
|
||||
*/
|
||||
|
||||
private import Semantic
|
||||
private import SemanticExprSpecific::SemanticExprConfig as Specific
|
||||
|
||||
class SemSsaVariable instanceof Specific::SsaVariable {
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
final Specific::Location getLocation() { result = super.getLocation() }
|
||||
|
||||
final SemExpr getAUse() { result = Specific::getAUse(this) }
|
||||
|
||||
final SemType getType() { result = Specific::getSsaVariableType(this) }
|
||||
|
||||
final SemBasicBlock getBasicBlock() { result = Specific::getSsaVariableBasicBlock(this) }
|
||||
}
|
||||
|
||||
class SemSsaExplicitUpdate extends SemSsaVariable {
|
||||
SemExpr sourceExpr;
|
||||
|
||||
SemSsaExplicitUpdate() { Specific::explicitUpdate(this, sourceExpr) }
|
||||
|
||||
final SemExpr getSourceExpr() { result = sourceExpr }
|
||||
}
|
||||
|
||||
class SemSsaPhiNode extends SemSsaVariable {
|
||||
SemSsaPhiNode() { Specific::phi(this) }
|
||||
|
||||
final SemSsaVariable getAPhiInput() { result = Specific::getAPhiInput(this) }
|
||||
}
|
||||
|
||||
class SemSsaReadPosition instanceof Specific::SsaReadPosition {
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
final Specific::Location getLocation() { result = super.getLocation() }
|
||||
|
||||
final predicate hasReadOfVar(SemSsaVariable var) { Specific::hasReadOfSsaVariable(this, var) }
|
||||
}
|
||||
|
||||
class SemSsaReadPositionPhiInputEdge extends SemSsaReadPosition {
|
||||
SemBasicBlock origBlock;
|
||||
SemBasicBlock phiBlock;
|
||||
|
||||
SemSsaReadPositionPhiInputEdge() { Specific::phiInputEdge(this, origBlock, phiBlock) }
|
||||
|
||||
predicate phiInput(SemSsaPhiNode phi, SemSsaVariable inp) { Specific::phiInput(this, phi, inp) }
|
||||
|
||||
SemBasicBlock getOrigBlock() { result = origBlock }
|
||||
|
||||
SemBasicBlock getPhiBlock() { result = phiBlock }
|
||||
}
|
||||
|
||||
class SemSsaReadPositionBlock extends SemSsaReadPosition {
|
||||
SemBasicBlock block;
|
||||
|
||||
SemSsaReadPositionBlock() { Specific::readBlock(this, block) }
|
||||
|
||||
SemBasicBlock getBlock() { result = block }
|
||||
|
||||
SemExpr getAnExpr() { result = getBlock().getAnExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along a back edge.
|
||||
*/
|
||||
predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge) {
|
||||
edge.phiInput(phi, inp) and
|
||||
// Conservatively assume that every edge is a back edge if we don't have dominance information.
|
||||
(
|
||||
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
|
||||
not edge.getOrigBlock().hasDominanceInformation()
|
||||
)
|
||||
}
|
||||
@@ -38,7 +38,7 @@ class SemType extends TSemType {
|
||||
* Gets a string that uniquely identifies this `SemType`. This string is often the same as the
|
||||
* result of `SemType.toString()`, but for some types it may be more verbose to ensure uniqueness.
|
||||
*/
|
||||
string getIdentityString() { result = this.toString() }
|
||||
string getIdentityString() { result = toString() }
|
||||
|
||||
/**
|
||||
* Gets the size of the type, in bytes, if known.
|
||||
@@ -132,7 +132,7 @@ class SemIntegerType extends SemNumericType {
|
||||
final predicate isSigned() { signed = true }
|
||||
|
||||
/** Holds if this integer type is unsigned. */
|
||||
final predicate isUnsigned() { not this.isSigned() }
|
||||
final predicate isUnsigned() { not isSigned() }
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* Simple constant analysis using the Semantic interface.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysisSpecific as Specific
|
||||
|
||||
/** An expression that always has the same integer value. */
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* C++-specific implementation of constant analysis.
|
||||
*/
|
||||
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
|
||||
/**
|
||||
* Gets the constant integer value of the specified expression, if any.
|
||||
*/
|
||||
int getIntConstantValue(SemExpr expr) { none() }
|
||||
@@ -0,0 +1,29 @@
|
||||
private import RangeAnalysisStage
|
||||
|
||||
module FloatDelta implements DeltaSig {
|
||||
class Delta = float;
|
||||
|
||||
bindingset[d]
|
||||
bindingset[result]
|
||||
float toFloat(Delta d) { result = d }
|
||||
|
||||
bindingset[d]
|
||||
bindingset[result]
|
||||
int toInt(Delta d) { result = d }
|
||||
|
||||
bindingset[n]
|
||||
bindingset[result]
|
||||
Delta fromInt(int n) { result = n }
|
||||
|
||||
bindingset[f]
|
||||
Delta fromFloat(float f) {
|
||||
result =
|
||||
min(float diff, float res |
|
||||
diff = (res - f) and res = f.ceil()
|
||||
or
|
||||
diff = (f - res) and res = f.floor()
|
||||
|
|
||||
res order by diff
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
private import ModulusAnalysisSpecific::Private
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import RangeAnalysisStage
|
||||
@@ -120,6 +120,13 @@ module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
private predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remainder of `val` modulo `mod`.
|
||||
*
|
||||
@@ -315,4 +322,20 @@ module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
private predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* C++-specific implementation of modulus analysis.
|
||||
*/
|
||||
module Private {
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
|
||||
predicate ignoreExprModulus(SemExpr e) { none() }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import RangeAnalysisImpl
|
||||
import experimental.semmle.code.cpp.semantic.SemanticBound
|
||||
@@ -0,0 +1,107 @@
|
||||
private import RangeAnalysisStage
|
||||
private import RangeAnalysisSpecific
|
||||
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
private import RangeUtils
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticBound as SemanticBound
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticLocation
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticSSA
|
||||
|
||||
module ConstantBounds implements BoundSig<FloatDelta> {
|
||||
class SemBound instanceof SemanticBound::SemBound {
|
||||
SemBound() {
|
||||
this instanceof SemanticBound::SemZeroBound
|
||||
or
|
||||
this.(SemanticBound::SemSsaBound).getAVariable() instanceof SemSsaPhiNode
|
||||
}
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
SemLocation getLocation() { result = super.getLocation() }
|
||||
|
||||
SemExpr getExpr(float delta) { result = super.getExpr(delta) }
|
||||
}
|
||||
|
||||
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
|
||||
|
||||
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
|
||||
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
}
|
||||
}
|
||||
|
||||
private module RelativeBounds implements BoundSig<FloatDelta> {
|
||||
class SemBound instanceof SemanticBound::SemBound {
|
||||
SemBound() { not this instanceof SemanticBound::SemZeroBound }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
SemLocation getLocation() { result = super.getLocation() }
|
||||
|
||||
SemExpr getExpr(float delta) { result = super.getExpr(delta) }
|
||||
}
|
||||
|
||||
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
|
||||
|
||||
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
|
||||
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
}
|
||||
}
|
||||
|
||||
private module ConstantStage =
|
||||
RangeStage<FloatDelta, ConstantBounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
private module RelativeStage =
|
||||
RangeStage<FloatDelta, RelativeBounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
private newtype TSemReason =
|
||||
TSemNoReason() or
|
||||
TSemCondReason(SemGuard guard) {
|
||||
guard = any(ConstantStage::SemCondReason reason).getCond()
|
||||
or
|
||||
guard = any(RelativeStage::SemCondReason reason).getCond()
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class SemReason extends TSemReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason, TSemNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason, TSemCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
SemGuard getCond() { this = TSemCondReason(result) }
|
||||
|
||||
override string toString() { result = getCond().toString() }
|
||||
}
|
||||
|
||||
private ConstantStage::SemReason constantReason(SemReason reason) {
|
||||
result instanceof ConstantStage::SemNoReason and reason instanceof SemNoReason
|
||||
or
|
||||
result.(ConstantStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
|
||||
}
|
||||
|
||||
private RelativeStage::SemReason relativeReason(SemReason reason) {
|
||||
result instanceof RelativeStage::SemNoReason and reason instanceof SemNoReason
|
||||
or
|
||||
result.(RelativeStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
|
||||
}
|
||||
|
||||
predicate semBounded(
|
||||
SemExpr e, SemanticBound::SemBound b, float delta, boolean upper, SemReason reason
|
||||
) {
|
||||
ConstantStage::semBounded(e, b, delta, upper, constantReason(reason))
|
||||
or
|
||||
RelativeStage::semBounded(e, b, delta, upper, relativeReason(reason))
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* C++-specific implementation of range analysis.
|
||||
*/
|
||||
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import RangeAnalysisStage
|
||||
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
|
||||
module CppLangImpl implements LangSig<FloatDelta> {
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreExprBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
predicate hasConstantBound(SemExpr e, float bound, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate hasBound(SemExpr e, SemExpr bound, float delta, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,8 @@
|
||||
* Provides utility predicates for range analysis.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import RangeAnalysisRelativeSpecific
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import RangeAnalysisSpecific
|
||||
private import RangeAnalysisStage as Range
|
||||
private import ConstantAnalysis
|
||||
|
||||
@@ -138,26 +138,3 @@ module RangeUtil<Range::DeltaSig D, Range::LangSig<D> Lang> implements Range::Ut
|
||||
not exists(Lang::getAlternateTypeForSsaVariable(var)) and result = var.getType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
|
||||
newtype TSign =
|
||||
TNeg() or
|
||||
@@ -73,7 +73,7 @@ class Sign extends TSign {
|
||||
* Gets a possible sign after subtracting an expression with sign `s` from an expression
|
||||
* that has this sign.
|
||||
*/
|
||||
Sign sub(Sign s) { result = this.add(s.neg()) }
|
||||
Sign sub(Sign s) { result = add(s.neg()) }
|
||||
|
||||
/**
|
||||
* Gets a possible sign after multiplying an expression with sign `s` to an expression
|
||||
@@ -231,37 +231,37 @@ class Sign extends TSign {
|
||||
or
|
||||
op instanceof Opcode::Store and result = this
|
||||
or
|
||||
op instanceof Opcode::AddOne and result = this.inc()
|
||||
op instanceof Opcode::AddOne and result = inc()
|
||||
or
|
||||
op instanceof Opcode::SubOne and result = this.dec()
|
||||
op instanceof Opcode::SubOne and result = dec()
|
||||
or
|
||||
op instanceof Opcode::Negate and result = this.neg()
|
||||
op instanceof Opcode::Negate and result = neg()
|
||||
or
|
||||
op instanceof Opcode::BitComplement and result = this.bitnot()
|
||||
op instanceof Opcode::BitComplement and result = bitnot()
|
||||
}
|
||||
|
||||
/** Perform `op` on this sign and sign `s`. */
|
||||
Sign applyBinaryOp(Sign s, Opcode op) {
|
||||
op instanceof Opcode::Add and result = this.add(s)
|
||||
op instanceof Opcode::Add and result = add(s)
|
||||
or
|
||||
op instanceof Opcode::Sub and result = this.sub(s)
|
||||
op instanceof Opcode::Sub and result = sub(s)
|
||||
or
|
||||
op instanceof Opcode::Mul and result = this.mul(s)
|
||||
op instanceof Opcode::Mul and result = mul(s)
|
||||
or
|
||||
op instanceof Opcode::Div and result = this.div(s)
|
||||
op instanceof Opcode::Div and result = div(s)
|
||||
or
|
||||
op instanceof Opcode::Rem and result = this.rem(s)
|
||||
op instanceof Opcode::Rem and result = rem(s)
|
||||
or
|
||||
op instanceof Opcode::BitAnd and result = this.bitand(s)
|
||||
op instanceof Opcode::BitAnd and result = bitand(s)
|
||||
or
|
||||
op instanceof Opcode::BitOr and result = this.bitor(s)
|
||||
op instanceof Opcode::BitOr and result = bitor(s)
|
||||
or
|
||||
op instanceof Opcode::BitXor and result = this.bitxor(s)
|
||||
op instanceof Opcode::BitXor and result = bitxor(s)
|
||||
or
|
||||
op instanceof Opcode::ShiftLeft and result = this.lshift(s)
|
||||
op instanceof Opcode::ShiftLeft and result = lshift(s)
|
||||
or
|
||||
op instanceof Opcode::ShiftRight and result = this.rshift(s)
|
||||
op instanceof Opcode::ShiftRight and result = rshift(s)
|
||||
or
|
||||
op instanceof Opcode::ShiftRightUnsigned and result = this.urshift(s)
|
||||
op instanceof Opcode::ShiftRightUnsigned and result = urshift(s)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import RangeAnalysisStage
|
||||
private import SignAnalysisSpecific as Specific
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import Sign
|
||||
@@ -198,16 +198,6 @@ module SignAnalysis<DeltaSig D, UtilSig<D> Utils> {
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression of an unsigned type. */
|
||||
private class UnsignedExpr extends FlowSignExpr {
|
||||
UnsignedExpr() { Utils::getTrackedType(this) instanceof SemUnsignedIntegerType }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
result = TPos() or
|
||||
result = TZero()
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
|
||||
binary.getLeftOperand() = left and binary.getRightOperand() = right
|
||||
@@ -338,11 +328,10 @@ module SignAnalysis<DeltaSig D, UtilSig<D> Utils> {
|
||||
* - `isEq = false` : `v != eqbound`
|
||||
*/
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity, SemExpr e |
|
||||
pos.hasReadOfVar(pragma[only_bind_into](v)) and
|
||||
semGuardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
|
||||
e = Utils::semSsaRead(pragma[only_bind_into](v), D::fromInt(0)) and
|
||||
guard.isEquality(eqbound, e, polarity) and
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue) and
|
||||
guard.isEquality(eqbound, Utils::semSsaRead(v, D::fromInt(0)), polarity) and
|
||||
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
|
||||
not unknownSign(eqbound)
|
||||
)
|
||||
@@ -2,7 +2,7 @@
|
||||
* Provides C++-specific definitions for use in sign analysis.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
|
||||
/**
|
||||
* Workaround to allow certain expressions to have a negative sign, even if the type of the
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Wrapper for the semantic range analysis library that mimics the
|
||||
* interface of the simple range analysis library.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticBound
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticExprSpecific
|
||||
private import RangeAnalysis
|
||||
|
||||
/**
|
||||
* Gets the lower bound of the expression.
|
||||
*
|
||||
* Note: expressions in C/C++ are often implicitly or explicitly cast to a
|
||||
* different result type. Such casts can cause the value of the expression
|
||||
* to overflow or to be truncated. This predicate computes the lower bound
|
||||
* of the expression without including the effect of the casts. To compute
|
||||
* the lower bound of the expression after all the casts have been applied,
|
||||
* call `lowerBound` like this:
|
||||
*
|
||||
* `lowerBound(expr.getFullyConverted())`
|
||||
*/
|
||||
float lowerBound(Expr expr) {
|
||||
exists(Instruction i, SemBound b | i.getAst() = expr and b instanceof SemZeroBound |
|
||||
semBounded(getSemanticExpr(i), b, result, false, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the upper bound of the expression.
|
||||
*
|
||||
* Note: expressions in C/C++ are often implicitly or explicitly cast to a
|
||||
* different result type. Such casts can cause the value of the expression
|
||||
* to overflow or to be truncated. This predicate computes the upper bound
|
||||
* of the expression without including the effect of the casts. To compute
|
||||
* the upper bound of the expression after all the casts have been applied,
|
||||
* call `upperBound` like this:
|
||||
*
|
||||
* `upperBound(expr.getFullyConverted())`
|
||||
*/
|
||||
float upperBound(Expr expr) {
|
||||
exists(Instruction i, SemBound b | i.getAst() = expr and b instanceof SemZeroBound |
|
||||
semBounded(getSemanticExpr(i), b, result, true, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the upper bound of `expr` may have been widened. This means the
|
||||
* upper bound is in practice likely to be overly wide.
|
||||
*/
|
||||
predicate upperBoundMayBeWidened(Expr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `expr` has a provably empty range. For example:
|
||||
*
|
||||
* 10 < expr and expr < 5
|
||||
*
|
||||
* The range of an expression can only be empty if it can never be
|
||||
* executed. For example:
|
||||
*
|
||||
* ```cpp
|
||||
* if (10 < x) {
|
||||
* if (x < 5) {
|
||||
* // Unreachable code
|
||||
* return x; // x has an empty range: 10 < x && x < 5
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate exprWithEmptyRange(Expr expr) { lowerBound(expr) > upperBound(expr) }
|
||||
|
||||
/** Holds if the definition might overflow negatively. */
|
||||
predicate defMightOverflowNegatively(RangeSsaDefinition def, StackVariable v) { none() }
|
||||
|
||||
/** Holds if the definition might overflow positively. */
|
||||
predicate defMightOverflowPositively(RangeSsaDefinition def, StackVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the definition might overflow (either positively or
|
||||
* negatively).
|
||||
*/
|
||||
predicate defMightOverflow(RangeSsaDefinition def, StackVariable v) {
|
||||
defMightOverflowNegatively(def, v) or
|
||||
defMightOverflowPositively(def, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow negatively. This predicate
|
||||
* does not consider the possibility that the expression might overflow
|
||||
* due to a conversion.
|
||||
*/
|
||||
predicate exprMightOverflowNegatively(Expr expr) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow negatively. Conversions
|
||||
* are also taken into account. For example the expression
|
||||
* `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than
|
||||
* due to the addition.
|
||||
*/
|
||||
predicate convertedExprMightOverflowNegatively(Expr expr) {
|
||||
exprMightOverflowNegatively(expr) or
|
||||
convertedExprMightOverflowNegatively(expr.getConversion())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow positively. This predicate
|
||||
* does not consider the possibility that the expression might overflow
|
||||
* due to a conversion.
|
||||
*/
|
||||
predicate exprMightOverflowPositively(Expr expr) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow positively. Conversions
|
||||
* are also taken into account. For example the expression
|
||||
* `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than
|
||||
* due to the addition.
|
||||
*/
|
||||
predicate convertedExprMightOverflowPositively(Expr expr) {
|
||||
exprMightOverflowPositively(expr) or
|
||||
convertedExprMightOverflowPositively(expr.getConversion())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow (either positively or
|
||||
* negatively). The possibility that the expression might overflow
|
||||
* due to an implicit or explicit cast is also considered.
|
||||
*/
|
||||
predicate convertedExprMightOverflow(Expr expr) {
|
||||
convertedExprMightOverflowNegatively(expr) or
|
||||
convertedExprMightOverflowPositively(expr)
|
||||
}
|
||||
@@ -18,10 +18,10 @@ external string selectedSourceFile();
|
||||
|
||||
class Cfg extends PrintAstConfiguration {
|
||||
/**
|
||||
* Holds if the AST for `decl` should be printed.
|
||||
* Print All declarations from the selected file.
|
||||
* Holds if the AST for `func` should be printed.
|
||||
* Print All functions from the selected file.
|
||||
*/
|
||||
override predicate shouldPrintDeclaration(Declaration decl) {
|
||||
decl.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
override predicate shouldPrintFunction(Function func) {
|
||||
func.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.9.2
|
||||
version: 0.6.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
warnOnImplicitThis: true
|
||||
|
||||
@@ -176,6 +176,20 @@ class Class extends UserType {
|
||||
/** Holds if this class, struct or union has a constructor. */
|
||||
predicate hasConstructor() { exists(this.getAConstructor()) }
|
||||
|
||||
/**
|
||||
* Holds if this class has a copy constructor that is either explicitly
|
||||
* declared (though possibly `= delete`) or is auto-generated, non-trivial
|
||||
* and called from somewhere.
|
||||
*
|
||||
* DEPRECATED: There is more than one reasonable definition of what it means
|
||||
* to have a copy constructor, and we do not want to promote one particular
|
||||
* definition by naming it with this predicate. Having a copy constructor
|
||||
* could mean that such a member is declared or defined in the source or that
|
||||
* it is callable by a particular caller. For C++11, there's also a question
|
||||
* of whether to include members that are defaulted or deleted.
|
||||
*/
|
||||
deprecated predicate hasCopyConstructor() { this.getAMemberFunction() instanceof CopyConstructor }
|
||||
|
||||
/**
|
||||
* Like accessOfBaseMember but returns multiple results if there are multiple
|
||||
* paths to `base` through the inheritance graph.
|
||||
|
||||
@@ -42,7 +42,7 @@ class Compilation extends @compilation {
|
||||
}
|
||||
|
||||
/** Gets a file compiled during this invocation. */
|
||||
File getAFileCompiled() { result = this.getFileCompiled(_) }
|
||||
File getAFileCompiled() { result = getFileCompiled(_) }
|
||||
|
||||
/** Gets the `i`th file compiled during this invocation */
|
||||
File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) }
|
||||
@@ -74,7 +74,7 @@ class Compilation extends @compilation {
|
||||
/**
|
||||
* Gets an argument passed to the extractor on this invocation.
|
||||
*/
|
||||
string getAnArgument() { result = this.getArgument(_) }
|
||||
string getAnArgument() { result = getArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th argument passed to the extractor on this invocation.
|
||||
|
||||
@@ -39,8 +39,7 @@ class Field extends MemberVariable {
|
||||
* complete most-derived object.
|
||||
*/
|
||||
int getAByteOffsetIn(Class mostDerivedClass) {
|
||||
result =
|
||||
mostDerivedClass.getABaseClassByteOffset(this.getDeclaringType()) + this.getByteOffset()
|
||||
result = mostDerivedClass.getABaseClassByteOffset(getDeclaringType()) + getByteOffset()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,10 +116,10 @@ class BitField extends Field {
|
||||
int getBitOffset() { fieldoffsets(underlyingElement(this), _, result) }
|
||||
|
||||
/** Holds if this bitfield is anonymous. */
|
||||
predicate isAnonymous() { this.hasName("(unnamed bitfield)") }
|
||||
predicate isAnonymous() { hasName("(unnamed bitfield)") }
|
||||
|
||||
override predicate isInitializable() {
|
||||
// Anonymous bitfields are not initializable.
|
||||
not this.isAnonymous()
|
||||
not isAnonymous()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,14 @@ class Container extends Locatable, @container {
|
||||
*/
|
||||
string getAbsolutePath() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets a URL representing the location of this container.
|
||||
*
|
||||
* For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
|
||||
*/
|
||||
deprecated string getURL() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* Gets the relative path of this file or folder from the root folder of the
|
||||
* analyzed source location. The relative path of the root folder itself is
|
||||
@@ -175,6 +183,12 @@ class Folder extends Container, @folder {
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Folder" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets the URL of this folder.
|
||||
*/
|
||||
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,6 +213,12 @@ class File extends Container, @file {
|
||||
result.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets the URL of this file.
|
||||
*/
|
||||
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/** Holds if this file was compiled as C (at any point). */
|
||||
predicate compiledAsC() { fileannotations(underlyingElement(this), 1, "compiled as c", "1") }
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ class LinkTarget extends @link_target {
|
||||
* captured as part of the snapshot, then everything is grouped together
|
||||
* into a single dummy link target.
|
||||
*/
|
||||
predicate isDummy() { this.getBinary().getAbsolutePath() = "" }
|
||||
predicate isDummy() { getBinary().getAbsolutePath() = "" }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getBinary().getAbsolutePath() }
|
||||
string toString() { result = getBinary().getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* Gets a function which was compiled into this link target, or had its
|
||||
|
||||
@@ -34,7 +34,7 @@ class Macro extends PreprocessorDirective, @ppd_define {
|
||||
* Gets the name of the macro. For example, `MAX` in
|
||||
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
|
||||
*/
|
||||
string getName() { result = this.getHead().regexpCapture("([^(]*+).*", 1) }
|
||||
string getName() { result = this.getHead().splitAt("(", 0) }
|
||||
|
||||
/** Holds if the macro has name `name`. */
|
||||
predicate hasName(string name) { this.getName() = name }
|
||||
|
||||
@@ -24,7 +24,7 @@ class NameQualifier extends NameQualifiableElement, @namequalifier {
|
||||
* Gets the expression ultimately qualified by the chain of name
|
||||
* qualifiers. For example, `f()` in `N1::N2::f()`.
|
||||
*/
|
||||
Expr getExpr() { result = this.getQualifiedElement+() }
|
||||
Expr getExpr() { result = getQualifiedElement+() }
|
||||
|
||||
/** Gets a location for this name qualifier. */
|
||||
override Location getLocation() { namequalifiers(underlyingElement(this), _, _, result) }
|
||||
@@ -56,12 +56,12 @@ class NameQualifier extends NameQualifiableElement, @namequalifier {
|
||||
if nqe instanceof SpecialNameQualifyingElement
|
||||
then
|
||||
exists(Access a |
|
||||
a = this.getQualifiedElement() and
|
||||
a = getQualifiedElement() and
|
||||
result = a.getTarget().getDeclaringType()
|
||||
)
|
||||
or
|
||||
exists(FunctionCall c |
|
||||
c = this.getQualifiedElement() and
|
||||
c = getQualifiedElement() and
|
||||
result = c.getTarget().getDeclaringType()
|
||||
)
|
||||
else result = nqe
|
||||
@@ -109,7 +109,7 @@ class NameQualifiableElement extends Element, @namequalifiableelement {
|
||||
* namespace.
|
||||
*/
|
||||
predicate hasGlobalQualifiedName() {
|
||||
this.getNameQualifier*().getQualifyingElement() instanceof GlobalNamespace
|
||||
getNameQualifier*().getQualifyingElement() instanceof GlobalNamespace
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +119,7 @@ class NameQualifiableElement extends Element, @namequalifiableelement {
|
||||
*/
|
||||
predicate hasSuperQualifiedName() {
|
||||
exists(NameQualifier nq, SpecialNameQualifyingElement snqe |
|
||||
nq = this.getNameQualifier*() and
|
||||
nq = getNameQualifier*() and
|
||||
namequalifiers(unresolveElement(nq), _, unresolveElement(snqe), _) and
|
||||
snqe.getName() = "__super"
|
||||
)
|
||||
@@ -164,5 +164,5 @@ library class SpecialNameQualifyingElement extends NameQualifyingElement,
|
||||
/** Gets the name of this special qualifying element. */
|
||||
override string getName() { specialnamequalifyingelements(underlyingElement(this), result) }
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
override string toString() { result = getName() }
|
||||
}
|
||||
|
||||
@@ -230,12 +230,8 @@ class GlobalNamespace extends Namespace {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C++ `std::` namespace and its inline namespaces.
|
||||
* The C++ `std::` namespace.
|
||||
*/
|
||||
class StdNamespace extends Namespace {
|
||||
StdNamespace() {
|
||||
this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace
|
||||
or
|
||||
this.isInline() and this.getParentNamespace() instanceof StdNamespace
|
||||
}
|
||||
StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace }
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class NestedFieldAccess extends FieldAccess {
|
||||
|
||||
NestedFieldAccess() {
|
||||
ultimateQualifier = getUltimateQualifier(this) and
|
||||
this.getTarget() = getANestedField(ultimateQualifier.getType().stripType())
|
||||
getTarget() = getANestedField(ultimateQualifier.getType().stripType())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user