Compare commits

..

2 Commits

Author SHA1 Message Date
Chris Smowton
463db799ba Update test expectations 2026-02-06 03:52:14 -08:00
Chris Smowton
d2636aad68 JS: Add missing rule to security-and-quality query list
Has header block

```
 * @id js/unhandled-error-in-stream-pipeline
 * @name Unhandled error in stream pipeline
 * @description Calling `pipe()` on a stream without error handling will drop errors coming from the input stream
 * @kind problem
 * @problem.severity warning
 * @precision high
 * @tags quality
 *       reliability
 *       error-handling
 *       frameworks/nodejs
```

... which would get selected by the security-and-quality selectors if we were using them. Any reason to exclude?
2026-02-06 11:24:30 +00:00
2614 changed files with 112421 additions and 158369 deletions

View File

@@ -11,8 +11,6 @@ build --compilation_mode opt
common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub
build --repo_env=CC=clang --repo_env=CXX=clang++
# Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues)
build --repo_env=ANDROID_HOME=
# print test output, like sembuild does.
# Set to `errors` if this is too verbose.
@@ -36,7 +34,7 @@ common --@rules_dotnet//dotnet/settings:strict_deps=false
common --@rules_rust//rust/toolchain/channel=nightly
# Reduce this eventually to empty, once we've fixed all our usages of java, and https://github.com/bazel-contrib/rules_go/issues/4193 is fixed
common --incompatible_autoload_externally="+@rules_cc,+@rules_java,+@rules_shell"
common --incompatible_autoload_externally="+@rules_java,+@rules_shell"
build --java_language_version=17
build --tool_java_language_version=17

View File

@@ -1 +1 @@
9.0.0
8.4.2

View File

@@ -45,5 +45,3 @@ updates:
directory: "/"
schedule:
interval: weekly
exclude-paths:
- "misc/bazel/registry/**"

78
.github/workflows/compile-queries.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: "Compile all queries using the latest stable CodeQL CLI"
on:
push:
branches: # makes sure the cache gets populated - running on the branches people tend to merge into.
- main
- "rc/*"
- "codeql-cli-*"
pull_request:
paths:
- '**.ql'
- '**.qll'
- '**/qlpack.yml'
- '**.dbscheme'
permissions:
contents: read
jobs:
detect-changes:
if: github.repository_owner == 'github'
runs-on: ubuntu-latest
outputs:
languages: ${{ steps.detect.outputs.languages }}
steps:
- uses: actions/checkout@v5
- name: Detect changed languages
id: detect
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
# For PRs, detect which languages have changes
changed_files=$(gh pr view ${{ github.event.pull_request.number }} --json files --jq '.files.[].path')
languages=()
for lang in actions cpp csharp go java javascript python ql ruby rust swift; do
if echo "$changed_files" | grep -qE "^($lang/|shared/)" ; then
languages+=("$lang")
fi
done
echo "languages=$(jq -c -n '$ARGS.positional' --args "${languages[@]}")" >> $GITHUB_OUTPUT
else
# For pushes to main/rc branches, run all languages
echo 'languages=["actions","cpp","csharp","go","java","javascript","python","ql","ruby","rust","swift"]' >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ github.token }}
compile-queries:
needs: detect-changes
if: github.repository_owner == 'github' && needs.detect-changes.outputs.languages != '[]'
runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
matrix:
language: ${{ fromJson(needs.detect-changes.outputs.languages) }}
steps:
- uses: actions/checkout@v5
- 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: ${{ matrix.language }}-queries
- name: check formatting
run: find shared ${{ matrix.language }}/ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 -n 3000 -P 10 codeql query format -q --check-only
- name: compile queries - check-only
# run with --check-only if running in a PR (github.sha != main)
if : ${{ github.event_name == 'pull_request' }}
shell: bash
run: codeql query compile -q -j0 ${{ matrix.language }}/ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500 --ram=56000
- name: compile queries - full
# do full compile if running on main - this populates the cache
if : ${{ github.event_name != 'pull_request' }}
shell: bash
run: codeql query compile -q -j0 ${{ matrix.language }}/ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500 --ram=56000

236
.github/workflows/ruby-build.yml vendored Normal file
View File

@@ -0,0 +1,236 @@
name: "Ruby: Build"
on:
push:
paths:
- "ruby/**"
- .github/workflows/ruby-build.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
- "shared/tree-sitter-extractor/**"
branches:
- main
- "rc/*"
pull_request:
paths:
- "ruby/**"
- .github/workflows/ruby-build.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
- "shared/tree-sitter-extractor/**"
branches:
- main
- "rc/*"
workflow_dispatch:
inputs:
tag:
description: "Version tag to create"
required: false
env:
CARGO_TERM_COLOR: always
defaults:
run:
working-directory: ruby
permissions:
contents: read
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- name: Install GNU tar
if: runner.os == 'macOS'
run: |
brew install gnu-tar
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
- name: Prepare Windows
if: runner.os == 'Windows'
shell: powershell
run: |
git config --global core.longpaths true
- uses: ./.github/actions/os-version
id: os_version
- name: Cache entire extractor
uses: actions/cache@v3
id: cache-extractor
with:
path: |
target/release/codeql-extractor-ruby
target/release/codeql-extractor-ruby.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') }}
- uses: actions/cache@v3
if: steps.cache-extractor.outputs.cache-hit != 'true'
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-rust-cargo-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/**/Cargo.lock') }}
- name: Check formatting
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd extractor && cargo fmt -- --check
- name: Build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd extractor && cargo build --verbose
- name: Run tests
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd extractor && cargo test --verbose
- name: Release build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd extractor && cargo build --release
- name: Generate dbscheme
if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
run: ../target/release/codeql-extractor-ruby generate --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v4
if: ${{ matrix.os == 'ubuntu-latest' }}
with:
name: ruby.dbscheme
path: ruby/ql/lib/ruby.dbscheme
- uses: actions/upload-artifact@v4
if: ${{ matrix.os == 'ubuntu-latest' }}
with:
name: TreeSitter.qll
path: ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v4
with:
name: extractor-${{ matrix.os }}
path: |
target/release/codeql-extractor-ruby
target/release/codeql-extractor-ruby.exe
retention-days: 1
compile-queries:
if: github.repository_owner == 'github'
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v5
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ruby-build
- name: Build Query Pack
run: |
PACKS=${{ runner.temp }}/query-packs
rm -rf $PACKS
codeql pack create ../misc/suite-helpers --output "$PACKS"
codeql pack create ../shared/regex --output "$PACKS"
codeql pack create ../shared/ssa --output "$PACKS"
codeql pack create ../shared/tutorial --output "$PACKS"
codeql pack create ql/lib --output "$PACKS"
codeql pack create -j0 ql/src --output "$PACKS" --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
PACK_FOLDER=$(readlink -f "$PACKS"/codeql/ruby-queries/*)
codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
(cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
- uses: actions/upload-artifact@v4
with:
name: codeql-ruby-queries
path: |
${{ runner.temp }}/query-packs/*
retention-days: 1
include-hidden-files: true
package:
runs-on: ubuntu-latest
needs: [build, compile-queries]
steps:
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
with:
name: ruby.dbscheme
path: ruby/ruby
- uses: actions/download-artifact@v4
with:
name: extractor-ubuntu-latest
path: ruby/linux64
- uses: actions/download-artifact@v4
with:
name: extractor-windows-latest
path: ruby/win64
- uses: actions/download-artifact@v4
with:
name: extractor-macos-latest
path: ruby/osx64
- run: |
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
zip -rq codeql-ruby.zip ruby
- uses: actions/upload-artifact@v4
with:
name: codeql-ruby-pack
path: ruby/codeql-ruby.zip
retention-days: 1
include-hidden-files: true
- uses: actions/download-artifact@v4
with:
name: codeql-ruby-queries
path: ruby/qlpacks
- run: |
echo '{
"provide": [
"ruby/codeql-extractor.yml",
"qlpacks/*/*/*/qlpack.yml"
]
}' > .codeqlmanifest.json
zip -rq codeql-ruby-bundle.zip .codeqlmanifest.json ruby qlpacks
- uses: actions/upload-artifact@v4
with:
name: codeql-ruby-bundle
path: ruby/codeql-ruby-bundle.zip
retention-days: 1
include-hidden-files: true
test:
defaults:
run:
working-directory: ${{ github.workspace }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
needs: [package]
steps:
- uses: actions/checkout@v5
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql
- name: Download Ruby bundle
uses: actions/download-artifact@v4
with:
name: codeql-ruby-bundle
path: ${{ runner.temp }}
- name: Unzip Ruby bundle
shell: bash
run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip"
- name: Run QL test
shell: bash
run: |
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" ruby/ql/test/library-tests/ast/constants/
- name: Create database
shell: bash
run: |
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root ruby/ql/test/library-tests/ast/constants/ ../database
- name: Analyze database
shell: bash
run: |
codeql database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls

View File

@@ -0,0 +1,75 @@
name: "Ruby: Collect database stats"
on:
push:
branches:
- main
- "rc/*"
paths:
- ruby/ql/lib/ruby.dbscheme
- .github/workflows/ruby-dataset-measure.yml
pull_request:
branches:
- main
- "rc/*"
paths:
- ruby/ql/lib/ruby.dbscheme
- .github/workflows/ruby-dataset-measure.yml
workflow_dispatch:
permissions:
contents: read
jobs:
measure:
env:
CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI
strategy:
fail-fast: false
matrix:
repo: [rails/rails, discourse/discourse, spree/spree, ruby/ruby]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Checkout ${{ matrix.repo }}
uses: actions/checkout@v5
with:
repository: ${{ matrix.repo }}
path: ${{ github.workspace }}/repo
- name: Create database
run: |
codeql database create \
--search-path "${{ github.workspace }}" \
--threads 4 \
--language ruby --source-root "${{ github.workspace }}/repo" \
"${{ runner.temp }}/database"
- name: Measure database
run: |
mkdir -p "stats/${{ matrix.repo }}"
codeql dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ruby"
- uses: actions/upload-artifact@v4
with:
name: measurements-${{ hashFiles('stats/**') }}
path: stats
retention-days: 1
merge:
runs-on: ubuntu-latest
needs: measure
steps:
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
with:
path: stats
- run: |
python -m pip install --user lxml
find stats -name 'stats.xml' | sort | xargs python ruby/scripts/merge_stats.py --output ruby/ql/lib/ruby.dbscheme.stats --normalise ruby_tokeninfo
- uses: actions/upload-artifact@v4
with:
name: ruby.dbscheme.stats
path: ruby/ql/lib/ruby.dbscheme.stats

40
.github/workflows/ruby-qltest-rtjo.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: "Ruby: Run RTJO Language Tests"
on:
pull_request:
types:
- opened
- synchronize
- reopened
- labeled
env:
CARGO_TERM_COLOR: always
defaults:
run:
working-directory: ruby
permissions:
contents: read
jobs:
qltest-rtjo:
if: "github.repository_owner == 'github' && github.event.label.name == 'Run: RTJO Language Tests'"
runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ruby-qltest
- name: Run QL tests
run: |
codeql test run --dynamic-join-order-mode=all --threads=0 --ram 50000 --search-path "${{ github.workspace }}" --check-databases --check-diff-informed --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
GITHUB_TOKEN: ${{ github.token }}

73
.github/workflows/ruby-qltest.yml vendored Normal file
View File

@@ -0,0 +1,73 @@
name: "Ruby: Run QL Tests"
on:
push:
paths:
- "ruby/**"
- "shared/**"
- .github/workflows/ruby-build.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
- "rc/*"
pull_request:
paths:
- "ruby/**"
- "shared/**"
- .github/workflows/ruby-qltest.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
- "rc/*"
env:
CARGO_TERM_COLOR: always
defaults:
run:
working-directory: ruby
permissions:
contents: read
jobs:
qlupgrade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/fetch-codeql
- name: Check DB upgrade scripts
run: |
echo >empty.trap
codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
codeql dataset upgrade testdb --additional-packs ql/lib
diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
- name: Check DB downgrade scripts
run: |
echo >empty.trap
rm -rf testdb; codeql dataset import -S ql/lib/ruby.dbscheme testdb empty.trap
codeql resolve upgrades --format=lines --allow-downgrades --additional-packs downgrades \
--dbscheme=ql/lib/ruby.dbscheme --target-dbscheme=downgrades/initial/ruby.dbscheme |
xargs codeql execute upgrades testdb
diff -q testdb/ruby.dbscheme downgrades/initial/ruby.dbscheme
qltest:
if: github.repository_owner == 'github'
runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ruby-qltest
- name: Run QL tests
run: |
codeql test run --threads=0 --ram 50000 --search-path "${{ github.workspace }}" --check-databases --check-diff-informed --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -15,23 +15,21 @@ local_path_override(
# see https://registry.bazel.build/ for a list of available packages
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "rules_go", version = "0.60.0")
bazel_dep(name = "rules_java", version = "9.6.1")
bazel_dep(name = "rules_pkg", version = "1.2.0")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
bazel_dep(name = "rules_python", version = "1.9.0")
bazel_dep(name = "rules_shell", version = "0.7.1")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "abseil-cpp", version = "20260107.1", repo_name = "absl")
bazel_dep(name = "rules_go", version = "0.56.1")
bazel_dep(name = "rules_pkg", version = "1.0.1")
bazel_dep(name = "rules_nodejs", version = "6.2.0-codeql.1")
bazel_dep(name = "rules_python", version = "0.40.0")
bazel_dep(name = "rules_shell", version = "0.5.0")
bazel_dep(name = "bazel_skylib", version = "1.8.1")
bazel_dep(name = "abseil-cpp", version = "20240116.1", repo_name = "absl")
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
bazel_dep(name = "fmt", version = "12.1.0-codeql.1")
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
bazel_dep(name = "gazelle", version = "0.47.0")
bazel_dep(name = "rules_kotlin", version = "2.2.0-codeql.1")
bazel_dep(name = "gazelle", version = "0.40.0")
bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rules_rust", version = "0.69.0")
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
bazel_dep(name = "rules_rust", version = "0.66.0")
bazel_dep(name = "zstd", version = "1.5.5.bcr.1")
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
@@ -43,7 +41,7 @@ RUST_EDITION = "2024"
# a nightly toolchain is required to enable experimental_use_cc_common_link, which we require internally
# we prefer to run the same version as internally, even if experimental_use_cc_common_link is not really
# required in this repo
RUST_VERSION = "nightly/2026-01-22"
RUST_VERSION = "nightly/2025-08-01"
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
@@ -55,26 +53,26 @@ rust.toolchain(
],
# generated by buildutils-internal/scripts/fill-rust-sha256s.py (internal repo)
sha256s = {
"2026-01-22/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "88db619323cc1321630d124efa51ed02fabc5e020f08cfa0eda2c0ac1afbe69a",
"2026-01-22/rustc-nightly-x86_64-apple-darwin.tar.xz": "08484da3fa38db56f93629aeabdc0ae9ff8ed9704c0792d35259cbc849b3f54c",
"2026-01-22/rustc-nightly-aarch64-apple-darwin.tar.xz": "a39c0b21b7058e364ea1bd43144e42e4bf1efade036b2e82455f2afce194ee81",
"2026-01-22/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "d00248ee9850dbb6932b2578e32ff74fc7c429854c1aa071066ca31b65385a3b",
"2026-01-22/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "70656a0ce994ffff16d5a35a7b170a0acd41e9bb54a589c96ed45bf97b094a4d",
"2026-01-22/clippy-nightly-x86_64-apple-darwin.tar.xz": "fe242519fa961522734733009705aec3c2d9a20cc57291f2aa614e5e6262c88f",
"2026-01-22/clippy-nightly-aarch64-apple-darwin.tar.xz": "38bb226363ec97c9722edf966cd58774a683e19fd2ff2a6030094445d51e06f9",
"2026-01-22/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "6da9b4470beea67abfebf046f141eee0d2a8db7c7a9e4e2294478734fd477228",
"2026-01-22/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "99004e9d10c43a01499642f53bb3184d41137a95d65bfb217098840a9e79e892",
"2026-01-22/cargo-nightly-x86_64-apple-darwin.tar.xz": "6e021394cf8d8400ac6cfdfcef24e4d74f988e91eb8028b36de3a64ce3502990",
"2026-01-22/cargo-nightly-aarch64-apple-darwin.tar.xz": "4b2494cb69ab64132cddbc411a38ea9f1105e54d6f986e43168d54f79510c673",
"2026-01-22/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "c36613cf57407212d10d37b76e49a60ff42336e953cdff9e177283f530a83fc1",
"2026-01-22/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "0b123c5027dbd833aae6845ffe9bd07d309bf798746a7176aadaea68fbcbd05d",
"2026-01-22/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "a47864491ad5619158c950ab7570fb6e487d5117338585c27334d45824b406d8",
"2026-01-22/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "db9bc826d6e2e7e914505d50157682e516ceb90357e83d77abddc32c2d962f41",
"2026-01-22/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "ffaa406932b2fe62e01dad61cf4ed34860a5d2a6f9306ca340d79e630d930039",
"2026-01-22/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "e9c0d5e06e18a4b509391b3088f29293e310cdc8ccc865be8fa3f09733326925",
"2026-01-22/rust-std-nightly-x86_64-apple-darwin.tar.xz": "25d75995cee679a4828ca9fe48c5a31a67c3b0846018440ef912e5a6208f53f6",
"2026-01-22/rust-std-nightly-aarch64-apple-darwin.tar.xz": "e4132bf3f2eed4684c86756a02315bcf481c23e675e3e25630fc604c9cb4594c",
"2026-01-22/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "961bb535ef95ae8a5fa4e224cb94aff190f155c45a9bcf7a53e184b024aa41b1",
"2025-08-01/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "9bbeaf5d3fc7247d31463a9083aa251c995cc50662c8219e7a2254d76a72a9a4",
"2025-08-01/rustc-nightly-x86_64-apple-darwin.tar.xz": "c9ea539a8eff0d5d162701f99f9e1aabe14dd0dfb420d62362817a5d09219de7",
"2025-08-01/rustc-nightly-aarch64-apple-darwin.tar.xz": "ae83feebbc39cfd982e4ecc8297731fe79c185173aee138467b334c5404b3773",
"2025-08-01/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "9f170c30d802a349be60cf52ec46260802093cb1013ad667fc0d528b7b10152f",
"2025-08-01/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "9ae5f3cd8f557c4f6df522597c69d14398cf604cfaed2b83e767c4b77a7eaaf6",
"2025-08-01/clippy-nightly-x86_64-apple-darwin.tar.xz": "983cb9ee0b6b968188e04ab2d33743d54764b2681ce565e1b3f2b9135c696a3e",
"2025-08-01/clippy-nightly-aarch64-apple-darwin.tar.xz": "ed2219dbc49d088225e1b7c5c4390fa295066e071fddaa2714018f6bb39ddbf0",
"2025-08-01/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "911f40ab5cbdd686f40e00965271fe47c4805513a308ed01f30eafb25b448a50",
"2025-08-01/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "106463c284e48e4904c717471eeec2be5cc83a9d2cae8d6e948b52438cad2e69",
"2025-08-01/cargo-nightly-x86_64-apple-darwin.tar.xz": "6ad35c40efc41a8c531ea43235058347b6902d98a9693bf0aed7fc16d5590cef",
"2025-08-01/cargo-nightly-aarch64-apple-darwin.tar.xz": "dd28c365e9d298abc3154c797720ad36a0058f131265c9978b4c8e4e37012c8a",
"2025-08-01/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "7b431286e12d6b3834b038f078389a00cac73f351e8c3152b2504a3c06420b3b",
"2025-08-01/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "e342e305d7927cc288d386983b2bc253cfad3776b113386e903d0b302648ef47",
"2025-08-01/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "e44dd3506524d85c37b3a54bcc91d01378fd2c590b2db5c5974d12f05c1b84d1",
"2025-08-01/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "0c1b5f46dd81be4a9227b10283a0fcaa39c14fea7e81aea6fd6d9887ff6cdc41",
"2025-08-01/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "423e5fd11406adccbc31b8456ceb7375ce055cdf45e90d2c3babeb2d7f58383f",
"2025-08-01/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "3c0ceb46a252647a1d4c7116d9ccae684fa5e42aaf3296419febd2c962c3b41d",
"2025-08-01/rust-std-nightly-x86_64-apple-darwin.tar.xz": "3be416003cab10f767390a753d1d16ae4d26c7421c03c98992cf1943e5b0efe8",
"2025-08-01/rust-std-nightly-aarch64-apple-darwin.tar.xz": "4046ac0ef951cb056b5028a399124f60999fa37792eab69d008d8d7965f389b4",
"2025-08-01/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "191ed9d8603c3a4fe5a7bbbc2feb72049078dae2df3d3b7d5dedf3abbf823e6e",
},
versions = [RUST_VERSION],
)
@@ -190,15 +188,6 @@ pip.parse(
)
use_repo(pip, "codegen_deps")
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(
is_default = True,
python_version = "3.12",
)
use_repo(python, "python_3_12", "python_versions")
register_toolchains("@python_versions//3.12:all")
swift_deps = use_extension("//swift/third_party:load.bzl", "swift_deps")
# following list can be kept in sync with `bazel mod tidy`
@@ -265,11 +254,11 @@ use_repo(
)
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.26.0")
go_sdk.download(version = "1.25.0")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//go/extractor:go.mod")
use_repo(go_deps, "com_github_stretchr_testify", "org_golang_x_mod", "org_golang_x_tools")
use_repo(go_deps, "org_golang_x_mod", "org_golang_x_tools")
ripunzip_archive = use_repo_rule("//misc/ripunzip:ripunzip.bzl", "ripunzip_archive")

View File

@@ -1,23 +1,3 @@
## 0.4.32
No user-facing changes.
## 0.4.31
No user-facing changes.
## 0.4.30
No user-facing changes.
## 0.4.29
No user-facing changes.
## 0.4.28
No user-facing changes.
## 0.4.27
### Bug Fixes

View File

@@ -1,3 +0,0 @@
## 0.4.28
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.4.29
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.4.30
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.4.31
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.4.32
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.4.32
lastReleaseVersion: 0.4.27

View File

@@ -1,5 +1,5 @@
name: codeql/actions-all
version: 0.4.33-dev
version: 0.4.28-dev
library: true
warnOnImplicitThis: true
dependencies:

View File

@@ -1,23 +1,3 @@
## 0.6.24
No user-facing changes.
## 0.6.23
No user-facing changes.
## 0.6.22
No user-facing changes.
## 0.6.21
No user-facing changes.
## 0.6.20
No user-facing changes.
## 0.6.19
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.6.20
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.6.21
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.6.22
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.6.23
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 0.6.24
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.24
lastReleaseVersion: 0.6.19

View File

@@ -1,5 +1,5 @@
name: codeql/actions-queries
version: 0.6.25-dev
version: 0.6.20-dev
library: false
warnOnImplicitThis: true
groups: [actions, queries]

View File

@@ -199,7 +199,6 @@ def annotate_as_appropriate(filename, lines):
# as overlay[local?]. It is not clear that these heuristics are exactly what we want,
# but they seem to work well enough for now (as determined by speed and accuracy numbers).
if (filename.endswith("Test.qll") or
re.search(r"go/ql/lib/semmle/go/security/[^/]+[.]qll$", filename.replace(os.sep, "/")) or
((filename.endswith("Query.qll") or filename.endswith("Config.qll")) and
any("implements DataFlow::ConfigSig" in line for line in lines))):
return None

View File

@@ -172,6 +172,10 @@
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
],
"C# ControlFlowReachability": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
],
"C++ ExternalAPIs": [
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"

View File

@@ -1,21 +0,0 @@
class Element extends @element {
string toString() { none() }
}
class Trap extends @trap {
string toString() { none() }
}
class Tag extends @tag {
string toString() { none() }
}
from Element e, Trap trap
where
in_trap_or_tag(e, trap)
or
exists(Tag tag |
in_trap_or_tag(e, tag) and
trap_uses_tag(trap, tag)
)
select e, trap

View File

@@ -1,13 +0,0 @@
class SourceFile extends @source_file {
string toString() { none() }
}
class Trap extends @trap {
string toString() { none() }
}
from SourceFile source_file, string name, Trap trap
where
source_file_uses_trap(source_file, trap) and
source_file_name(source_file, name)
select name, trap

View File

@@ -1,8 +0,0 @@
description: Add source_file_name
compatibility: backwards
source_file_uses_trap.rel: run source_file_uses_trap.ql
source_file_name.rel: delete
tag_name.rel: delete
trap_uses_tag.rel: delete
in_trap.rel: run in_trap.ql
in_trap_or_tag.rel: delete

View File

@@ -1,5 +0,0 @@
description: Add trap_filename, source_file_uses_trap and in_trap relations
compatibility: full
trap_filename.rel: delete
source_file_uses_trap.rel: delete
in_trap.rel: delete

View File

@@ -52,6 +52,5 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql

View File

@@ -160,7 +160,6 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql
ql/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql

View File

@@ -93,6 +93,5 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql

View File

@@ -1,38 +1,3 @@
## 8.0.3
No user-facing changes.
## 8.0.2
No user-facing changes.
## 8.0.1
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
## 8.0.0
### Breaking Changes
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
### Minor Analysis Improvements
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
### Bug Fixes
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
## 7.1.1
### Minor Analysis Improvements
* Added remote flow source models for the `winhttp.h` windows header and the Azure SDK core library for C/C++.
## 7.1.0
### New Features

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added remote flow source models for the `winhttp.h` windows header and the Azure SDK core library for C/C++.

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* Added a class `IndirectUninitializedNode` to represent the indirection of an uninitialized local variable as a dataflow node.

View File

@@ -1,5 +0,0 @@
---
category: feature
---
* Added a class `DataFlow::IndirectParameterNode` to represent the indirection of a parameter as a dataflow node.
* Added a predicate `Node::asIndirectInstruction` which returns the `Instruction` that defines the indirect dataflow node, if any.

View File

@@ -1,5 +0,0 @@
---
category: feature
---
* Added a class `ConstructorDirectFieldInit` to represent field initializations that occur in member initializer lists.
* Added a class `ConstructorDefaultFieldInit` to represent default field initializations.

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
* The `SourceModelCsv`, `SinkModelCsv`, and `SummaryModelCsv` classes and the associated CSV parsing infrastructure have been removed from `ExternalFlow.qll`. New models should be added as `.model.yml` files in the `ext/` directory.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added dataflow through members initialized via non-static data member initialization (NSDMI).

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added `HttpReceiveHttpRequest`, `HttpReceiveRequestEntityBody`, and `HttpReceiveClientCertificate` from Win32's `http.h` as remote flow sources.

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* Added a subclass `MesonPrivateTestFile` of `ConfigurationTestFile` that represents files created by Meson to test the build configuration.

View File

@@ -1,5 +0,0 @@
## 7.1.1
### Minor Analysis Improvements
* Added remote flow source models for the `winhttp.h` windows header and the Azure SDK core library for C/C++.

View File

@@ -1,14 +0,0 @@
## 8.0.0
### Breaking Changes
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
### Minor Analysis Improvements
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
### Bug Fixes
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.

View File

@@ -1,5 +0,0 @@
## 8.0.1
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.

View File

@@ -1,3 +0,0 @@
## 8.0.2
No user-facing changes.

View File

@@ -1,3 +0,0 @@
## 8.0.3
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 8.0.3
lastReleaseVersion: 7.1.0

View File

@@ -31,9 +31,6 @@ extensions:
- ["", "", False, "WinHttpQueryHeadersEx", "", "", "Argument[*5]", "remote", "manual"]
- ["", "", False, "WinHttpQueryHeadersEx", "", "", "Argument[*6]", "remote", "manual"]
- ["", "", False, "WinHttpQueryHeadersEx", "", "", "Argument[**8]", "remote", "manual"]
- ["", "", False, "HttpReceiveHttpRequest", "", "", "Argument[*3]", "remote", "manual"]
- ["", "", False, "HttpReceiveRequestEntityBody", "", "", "Argument[*3]", "remote", "manual"]
- ["", "", False, "HttpReceiveClientCertificate", "", "", "Argument[*3]", "remote", "manual"]
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel

View File

@@ -1,22 +0,0 @@
# ZeroMQ networking library models
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: sourceModel
data: # namespace, type, subtypes, name, signature, ext, output, kind, provenance
- ["", "", False, "zmq_recv", "", "", "Argument[*1]", "remote", "manual"]
- ["", "", False, "zmq_recvmsg", "", "", "Argument[*1]", "remote", "manual"]
- ["", "", False, "zmq_msg_recv", "", "", "Argument[*0]", "remote", "manual"]
- addsTo:
pack: codeql/cpp-all
extensible: sinkModel
data: # namespace, type, subtypes, name, signature, ext, input, kind, provenance
- ["", "", False, "zmq_send", "", "", "Argument[*1]", "remote-sink", "manual"]
- ["", "", False, "zmq_sendmsg", "", "", "Argument[*1]", "remote-sink", "manual"]
- ["", "", False, "zmq_msg_send", "", "", "Argument[*0]", "remote-sink", "manual"]
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
- ["", "", False, "zmq_msg_init_data", "", "", "Argument[*1]", "Argument[*0]", "taint", "manual"]
- ["", "", False, "zmq_msg_data", "", "", "Argument[*0]", "ReturnValue[*]", "taint", "manual"]

View File

@@ -1,19 +0,0 @@
# Models for getc and similar character-reading functions
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: sourceModel
data: # namespace, type, subtypes, name, signature, ext, output, kind, provenance
- ["", "", False, "getc", "", "", "ReturnValue", "remote", "manual"]
- ["", "", False, "getwc", "", "", "ReturnValue", "remote", "manual"]
- ["", "", False, "_getc_nolock", "", "", "ReturnValue", "remote", "manual"]
- ["", "", False, "_getwc_nolock", "", "", "ReturnValue", "remote", "manual"]
- ["", "", False, "getch", "", "", "ReturnValue", "local", "manual"]
- ["", "", False, "_getch", "", "", "ReturnValue", "local", "manual"]
- ["", "", False, "_getwch", "", "", "ReturnValue", "local", "manual"]
- ["", "", False, "_getch_nolock", "", "", "ReturnValue", "local", "manual"]
- ["", "", False, "_getwch_nolock", "", "", "ReturnValue", "local", "manual"]
- ["", "", False, "getchar", "", "", "ReturnValue", "local", "manual"]
- ["", "", False, "getwchar", "", "", "ReturnValue", "local", "manual"]
- ["", "", False, "_getchar_nolock", "", "", "ReturnValue", "local", "manual"]
- ["", "", False, "_getwchar_nolock", "", "", "ReturnValue", "local", "manual"]

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 8.0.4-dev
version: 7.1.1-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -26,19 +26,3 @@ class CmakeTryCompileFile extends ConfigurationTestFile {
)
}
}
/**
* A file created by Meson to test the system configuration.
*/
class MesonPrivateTestFile extends ConfigurationTestFile {
MesonPrivateTestFile() {
this.getBaseName() = "testfile.c" and
exists(Folder folder, Folder parent |
folder = this.getParentContainer() and
parent = folder.getParentContainer()
|
folder.getBaseName().matches("tmp%") and
parent.getBaseName() = "meson-private"
)
}
}

View File

@@ -524,12 +524,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
not exists(NewOrNewArrayExpr new | e = new.getAllocatorCall().getArgument(0))
)
}
/**
* Holds if this function has an ambiguous return type, meaning that zero or multiple return
* types for this function are present in the database (this can occur in `build-mode: none`).
*/
predicate hasAmbiguousReturnType() { count(this.getType()) != 1 }
}
pragma[noinline]

View File

@@ -14,9 +14,7 @@ class PackedTimeType extends Type {
}
}
private predicate timeType(string typeName) {
typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm", "TIME_FIELDS", "_TIME_FIELDS", "PTIME_FIELDS"]
}
private predicate timeType(string typeName) { typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm"] }
/**
* A type that is used to represent times and dates in an 'unpacked' form, that is,
@@ -97,24 +95,3 @@ class StructTmMonthFieldAccess extends MonthFieldAccess {
class StructTmYearFieldAccess extends YearFieldAccess {
StructTmYearFieldAccess() { this.getTarget().getName() = "tm_year" }
}
/**
* A `DayFieldAccess` for the `TIME_FIELDS` struct.
*/
class TimeFieldsDayFieldAccess extends DayFieldAccess {
TimeFieldsDayFieldAccess() { this.getTarget().getName() = "Day" }
}
/**
* A `MonthFieldAccess` for the `TIME_FIELDS` struct.
*/
class TimeFieldsMonthFieldAccess extends MonthFieldAccess {
TimeFieldsMonthFieldAccess() { this.getTarget().getName() = "Month" }
}
/**
* A `YearFieldAccess` for the `TIME_FIELDS` struct.
*/
class TimeFieldsYearFieldAccess extends YearFieldAccess {
TimeFieldsYearFieldAccess() { this.getTarget().getName() = "Year" }
}

View File

@@ -163,23 +163,12 @@ predicate primitiveVariadicFormatter(
)
}
/**
* Gets a function call whose target is a variadic formatter with the given
* `type`, `format` parameter index and `output` parameter index.
*
* Join-order helper for `callsVariadicFormatter`.
*/
pragma[nomagic]
private predicate callsVariadicFormatterCall(FunctionCall fc, string type, int format, int output) {
variadicFormatter(fc.getTarget(), type, format, output)
}
private predicate callsVariadicFormatter(
Function f, string type, int formatParamIndex, int outputParamIndex
) {
// calls a variadic formatter with `formatParamIndex`, `outputParamIndex` linked
exists(FunctionCall fc, int format, int output |
callsVariadicFormatterCall(fc, type, format, output) and
variadicFormatter(pragma[only_bind_into](fc.getTarget()), type, format, output) and
fc.getEnclosingFunction() = f and
fc.getArgument(format) = f.getParameter(formatParamIndex).getAnAccess() and
fc.getArgument(output) = f.getParameter(outputParamIndex).getAnAccess()
@@ -187,7 +176,7 @@ private predicate callsVariadicFormatter(
or
// calls a variadic formatter with only `formatParamIndex` linked
exists(FunctionCall fc, string calledType, int format, int output |
callsVariadicFormatterCall(fc, calledType, format, output) and
variadicFormatter(pragma[only_bind_into](fc.getTarget()), calledType, format, output) and
fc.getEnclosingFunction() = f and
fc.getArgument(format) = f.getParameter(formatParamIndex).getAnAccess() and
not fc.getArgument(output) = f.getParameter(_).getAnAccess() and

View File

@@ -1663,7 +1663,7 @@ private module Cached {
private predicate compares_ge(
ValueNumber test, Operand left, Operand right, int k, boolean isGe, GuardValue value
) {
compares_lt(test, right, left, 1 - k, isGe, value)
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, value))
}
/** Rearrange various simple comparisons into `left < right + k` form. */

View File

@@ -1,10 +1,9 @@
/**
* INTERNAL use only. This is an experimental API subject to change without notice.
*
* Provides classes and predicates for dealing with flow models specified
* in data extension files.
* Provides classes and predicates for dealing with flow models specified in CSV format.
*
* The extensible relations have the following columns:
* The CSV specification has the following columns:
* - Sources:
* `namespace; type; subtypes; name; signature; ext; output; kind`
* - Sinks:
@@ -105,9 +104,117 @@ private import internal.FlowSummaryImpl::Private
private import internal.FlowSummaryImpl::Private::External
private import internal.ExternalFlowExtensions::Extensions as Extensions
private import codeql.mad.ModelValidation as SharedModelVal
private import codeql.util.Unit
private import codeql.mad.static.ModelsAsData as SharedMaD
/**
* A unit class for adding additional source model rows.
*
* Extend this class to add additional source definitions.
*/
class SourceModelCsv extends Unit {
/** Holds if `row` specifies a source definition. */
abstract predicate row(string row);
}
/**
* A unit class for adding additional sink model rows.
*
* Extend this class to add additional sink definitions.
*/
class SinkModelCsv extends Unit {
/** Holds if `row` specifies a sink definition. */
abstract predicate row(string row);
}
/**
* A unit class for adding additional summary model rows.
*
* Extend this class to add additional flow summary definitions.
*/
class SummaryModelCsv extends Unit {
/** Holds if `row` specifies a summary definition. */
abstract predicate row(string row);
}
/** Holds if `row` is a source model. */
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
/** Holds if `row` is a sink model. */
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
/** Holds if `row` is a summary model. */
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
private module MadInput implements SharedMaD::InputSig {
/** Holds if a source model exists for the given parameters. */
predicate additionalSourceModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string output, string kind, string provenance, string model
) {
exists(string row |
sourceModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = output and
row.splitAt(";", 7) = kind
) and
provenance = "manual" and
model = ""
}
/** Holds if a sink model exists for the given parameters. */
predicate additionalSinkModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string kind, string provenance, string model
) {
exists(string row |
sinkModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = input and
row.splitAt(";", 7) = kind
) and
provenance = "manual" and
model = ""
}
/**
* Holds if a summary model exists for the given parameters.
*
* This predicate does not expand `@` to `*`s.
*/
predicate additionalSummaryModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string output, string kind, string provenance, string model
) {
exists(string row |
summaryModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = input and
row.splitAt(";", 7) = output and
row.splitAt(";", 8) = kind
) and
provenance = "manual" and
model = ""
}
string namespaceSegmentSeparator() { result = "::" }
}
@@ -143,8 +250,8 @@ predicate summaryModel(
)
}
/** Provides a query predicate to check the data for validation errors. */
module ModelValidation {
/** Provides a query predicate to check the CSV data for validation errors. */
module CsvValidation {
private string getInvalidModelInput() {
exists(string pred, AccessPath input, string part |
sinkModel(_, _, _, _, _, _, input, _, _, _) and pred = "sink"
@@ -187,6 +294,40 @@ module ModelValidation {
private module KindVal = SharedModelVal::KindValidation<KindValConfig>;
private string getInvalidModelSubtype() {
exists(string pred, string row |
sourceModel(row) and pred = "source"
or
sinkModel(row) and pred = "sink"
or
summaryModel(row) and pred = "summary"
|
exists(string b |
b = row.splitAt(";", 2) and
not b = ["true", "false"] and
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
)
)
}
private string getInvalidModelColumnCount() {
exists(string pred, string row, int expect |
sourceModel(row) and expect = 8 and pred = "source"
or
sinkModel(row) and expect = 8 and pred = "sink"
or
summaryModel(row) and expect = 9 and pred = "summary"
|
exists(int cols |
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
cols != expect and
result =
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
"."
)
)
}
private string getInvalidModelSignature() {
exists(string pred, string namespace, string type, string name, string signature, string ext |
sourceModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "source"
@@ -212,25 +353,12 @@ module ModelValidation {
)
}
private string getIncorrectConstructorSummaryOutput() {
exists(string namespace, string type, string name, string output |
type = name or
type = name + "<" + any(string s)
|
summaryModel(namespace, type, _, name, _, _, _, output, _, _, _) and
output.matches("ReturnValue%") and
result =
"Constructor model for " + namespace + "." + type +
" should use `Argument[this]` in the output, not `ReturnValue`."
)
}
/** Holds if some row in a MaD flow model appears to contain typos. */
/** Holds if some row in a CSV-based flow model appears to contain typos. */
query predicate invalidModelRow(string msg) {
msg =
[
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
KindVal::getInvalidModelKind(), getIncorrectConstructorSummaryOutput()
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
]
}
}
@@ -427,7 +555,6 @@ private Locatable getSupportedFunctionTemplateArgument(Function templateFunction
* Normalize the `n`'th parameter of `f` by replacing template names
* with `func:N` (where `N` is the index of the template).
*/
pragma[nomagic]
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
exists(Function templateFunction |
templateFunction = getFullyTemplatedFunction(f) and
@@ -884,7 +1011,7 @@ private module Cached {
}
/**
* Holds if `node` is specified as a source with the given kind in a MaD flow
* Holds if `node` is specified as a source with the given kind in a CSV flow
* model.
*/
cached
@@ -895,7 +1022,7 @@ private module Cached {
}
/**
* Holds if `node` is specified as a sink with the given kind in a MaD flow
* Holds if `node` is specified as a sink with the given kind in a CSV flow
* model.
*/
cached

View File

@@ -201,7 +201,7 @@ module SourceSinkInterpretationInput implements
string toString() {
result = this.asElement().toString()
or
result = this.asNode().toStringImpl()
result = this.asNode().toString()
or
result = this.asCall().toString()
}

View File

@@ -585,15 +585,12 @@ class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit
/**
* An initialization of a member variable performed as part of a
* constructor's initializer list or by default initialization.
*
* constructor's explicit initializer list or implicit actions.
* In the example below, member variable `b` is being initialized by
* constructor parameter `a`, and `c` is initialized by default
* initialization:
* constructor parameter `a`:
* ```
* struct S {
* int b;
* int c = 3;
* S(int a): b(a) {}
* } s(2);
* ```
@@ -619,28 +616,6 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit {
override predicate mayBeGloballyImpure() { this.getExpr().mayBeGloballyImpure() }
}
/**
* An initialization of a member variable performed as part of a
* constructor's explicit initializer list.
*/
class ConstructorDirectFieldInit extends ConstructorFieldInit {
ConstructorDirectFieldInit() { exists(this.getChild(0)) }
override string getAPrimaryQlClass() { result = "ConstructorDirectFieldInit" }
}
/**
* An initialization of a member variable performed by default
* initialization.
*/
class ConstructorDefaultFieldInit extends ConstructorFieldInit {
ConstructorDefaultFieldInit() {
not exists(this.getChild(0)) and exists(this.getTarget().getInitializer())
}
override string getAPrimaryQlClass() { result = "ConstructorDefaultFieldInit" }
}
/**
* A call to a destructor of a base class or field as part of a destructor's
* compiler-generated actions.

View File

@@ -6,67 +6,85 @@ private import OverlayXml
/**
* Holds always for the overlay variant and never for the base variant.
* This local predicate is used to define local predicates that behave
* differently for the base and overlay variant.
*/
overlay[local]
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
overlay[local]
private string getLocationFilePath(@location_default loc) {
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
}
/**
* Holds if the TRAP file or tag `t` is reachable from source file `sourceFile`
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
* Gets the file path for an element with a single location.
*/
overlay[local]
private predicate locallyReachableTrapOrTag(
boolean isOverlayVariant, string sourceFile, @trap_or_tag t
) {
exists(@source_file sf, @trap trap |
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
source_file_uses_trap(sf, trap) and
source_file_name(sf, sourceFile) and
(t = trap or trap_uses_tag(trap, t))
private string getSingleLocationFilePath(@element e) {
exists(@location_default loc |
var_decls(e, _, _, _, loc)
or
fun_decls(e, _, _, _, loc)
or
type_decls(e, _, loc)
or
namespace_decls(e, _, loc, _)
or
macroinvocations(e, _, loc, _)
or
preprocdirects(e, _, loc)
|
result = getLocationFilePath(loc)
)
}
/**
* Holds if element `e` is in TRAP file or tag `t`
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
* Gets the file path for an element with potentially multiple locations.
*/
overlay[local]
private predicate locallyInTrapOrTag(boolean isOverlayVariant, @element e, @trap_or_tag t) {
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
in_trap_or_tag(e, t)
private string getMultiLocationFilePath(@element e) {
exists(@location_default loc |
var_decls(_, e, _, _, loc)
or
fun_decls(_, e, _, _, loc)
or
type_decls(_, e, loc)
or
namespace_decls(_, e, loc, _)
|
result = getLocationFilePath(loc)
)
}
/**
* A local helper predicate that holds in the base variant and never in the
* overlay variant.
*/
overlay[local]
private predicate isBase() { not isOverlay() }
/**
* Holds if `path` was extracted in the overlay database.
*/
overlay[local]
private predicate overlayHasFile(string path) {
isOverlay() and
files(_, path) and
path != ""
}
/**
* Discards an element from the base variant if:
* - We have knowledge about what TRAP file or tag it is in (in the base).
* - It is not in any overlay TRAP file or tag that is reachable from an overlay source file.
* - For every base TRAP file or tag that contains it and is reachable from a base source file,
* either the source file has changed, or the overlay has redefined the TRAP file or tag,
* or the overlay runner has re-extracted the same source file.
* - It has a single location in a file extracted in the overlay, or
* - All of its locations are in files extracted in the overlay.
*/
overlay[discard_entity]
private predicate discardElement(@element e) {
// If we don't have any knowledge about what TRAP file something
// is in, then we don't want to discard it, so we only consider
// entities that are known to be in a base TRAP file or tag.
locallyInTrapOrTag(false, e, _) and
// Anything that is reachable from an overlay source file should
// not be discarded.
not exists(@trap_or_tag t | locallyInTrapOrTag(true, e, t) |
locallyReachableTrapOrTag(true, _, t)
) and
// Finally, we have to make sure the base variant does not retain it.
// If it is reachable from a base source file, then that is
// sufficient unless either the base source file has changed (in
// particular, been deleted), or the overlay has redefined the TRAP
// file or tag it is in, or the overlay runner has re-extracted the same
// source file (e.g. because a header it includes has changed).
forall(@trap_or_tag t, string sourceFile |
locallyInTrapOrTag(false, e, t) and
locallyReachableTrapOrTag(false, sourceFile, t)
|
overlayChangedFiles(sourceFile) or
locallyReachableTrapOrTag(true, _, t) or
locallyReachableTrapOrTag(true, sourceFile, _)
isBase() and
(
overlayHasFile(getSingleLocationFilePath(e))
or
forex(string path | path = getMultiLocationFilePath(e) | overlayHasFile(path))
)
}

View File

@@ -8,145 +8,83 @@ private import cpp
private import semmle.code.cpp.ir.IR
/**
* Provides an inter-procedural must-flow data flow analysis.
* A configuration of a data flow analysis that performs must-flow analysis. This is different
* from `DataFlow.qll` which performs may-flow analysis (i.e., it finds paths where the source _may_
* flow to the sink).
*
* Like in `DataFlow.qll`, each use of the `MustFlow.qll` library must define its own unique extension
* of this abstract class. To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string and override `isSource`, `isSink` (and
* `isAdditionalFlowStep` if additional steps are required).
*/
module MustFlow {
/**
* An input configuration of a data flow analysis that performs must-flow analysis. This is different
* from `DataFlow.qll` which performs may-flow analysis (i.e., it finds paths where the source _may_
* flow to the sink).
*/
signature module ConfigSig {
/**
* Holds if `source` is a relevant data flow source.
*/
predicate isSource(Instruction source);
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(Operand sink);
/**
* Holds if data flow through `instr` is prohibited.
*/
default predicate isBarrier(Instruction instr) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
*/
default predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }
/** Holds if this configuration allows flow from arguments to parameters. */
default predicate allowInterproceduralFlow() { any() }
}
abstract class MustFlowConfiguration extends string {
bindingset[this]
MustFlowConfiguration() { any() }
/**
* Constructs a global must-flow computation.
* Holds if `source` is a relevant data flow source.
*/
module Global<ConfigSig Config> {
import Config
abstract predicate isSource(Instruction source);
/**
* Holds if data must flow from `source` to `sink`.
*
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate flowPath(PathNode source, PathSink sink) {
isSource(source.getInstruction()) and
source.getASuccessor*() = sink
}
/**
* Holds if `sink` is a relevant data flow sink.
*/
abstract predicate isSink(Operand sink);
/** Holds if `node` flows from a source. */
pragma[nomagic]
private predicate flowsFromSource(Instruction node) {
not isBarrier(node) and
(
isSource(node)
or
exists(Instruction mid |
step(mid, node) and
flowsFromSource(mid)
)
)
}
/**
* Holds if data flow through `instr` is prohibited.
*/
predicate isBarrier(Instruction instr) { none() }
/** Holds if `node` flows to a sink. */
pragma[nomagic]
private predicate flowsToSink(Instruction node) {
flowsFromSource(node) and
(
isSink(node.getAUse())
or
exists(Instruction mid |
step(node, mid) and
flowsToSink(mid)
)
)
}
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
*/
predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }
/** Holds if `nodeFrom` flows to `nodeTo`. */
private predicate step(Instruction nodeFrom, Instruction nodeTo) {
Cached::localStep(nodeFrom, nodeTo)
or
allowInterproceduralFlow() and
Cached::flowThroughCallable(nodeFrom, nodeTo)
or
isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
}
/** Holds if this configuration allows flow from arguments to parameters. */
predicate allowInterproceduralFlow() { any() }
private newtype TLocalPathNode =
MkLocalPathNode(Instruction n) {
flowsToSink(n) and
(
isSource(n)
or
exists(PathNode mid | step(mid.getInstruction(), n))
)
}
/** A `Node` that is in a path from a source to a sink. */
class PathNode extends TLocalPathNode {
Instruction n;
PathNode() { this = MkLocalPathNode(n) }
/** Gets the underlying node. */
Instruction getInstruction() { result = n }
/** Gets a textual representation of this node. */
string toString() { result = n.getAst().toString() }
/** Gets the location of this element. */
Location getLocation() { result = n.getLocation() }
/** Gets a successor node, if any. */
PathNode getASuccessor() { step(this.getInstruction(), result.getInstruction()) }
}
private class PathSink extends PathNode {
PathSink() { isSink(this.getInstruction().getAUse()) }
}
/**
* Provides the query predicates needed to include a graph in a path-problem query.
*/
module PathGraph {
private predicate reach(PathNode n) { n instanceof PathSink or reach(n.getASuccessor()) }
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
}
/**
* Holds if data must flow from `source` to `sink` for this configuration.
*
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
this.isSource(source.getInstruction()) and
source.getASuccessor*() = sink
}
}
/** Holds if `node` flows from a source. */
pragma[nomagic]
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
not config.isBarrier(node) and
(
config.isSource(node)
or
exists(Instruction mid |
step(mid, node, config) and
flowsFromSource(mid, pragma[only_bind_into](config))
)
)
}
/** Holds if `node` flows to a sink. */
pragma[nomagic]
private predicate flowsToSink(Instruction node, MustFlowConfiguration config) {
flowsFromSource(node, pragma[only_bind_into](config)) and
(
config.isSink(node.getAUse())
or
exists(Instruction mid |
step(node, mid, config) and
flowsToSink(mid, pragma[only_bind_into](config))
)
)
}
cached
private module Cached {
/** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */
@@ -164,7 +102,7 @@ private module Cached {
not f.isVirtual() and
call.getPositionalArgument(n) = instr and
f = call.getStaticCallTarget() and
isEnclosingNonVirtualFunctionInitializeParameter(init, f) and
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n))
}
@@ -173,7 +111,7 @@ private module Cached {
* corresponding initialization instruction that receives the value of `instr` in `f`.
*/
pragma[noinline]
private predicate isPositionalArgumentInitParam(
private predicate getPositionalArgumentInitParam(
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
) {
exists(int n |
@@ -188,18 +126,18 @@ private module Cached {
* `instr` in `f`.
*/
pragma[noinline]
private predicate isThisArgumentInitParam(
private predicate getThisArgumentInitParam(
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
) {
not f.isVirtual() and
call.getStaticCallTarget() = f and
isEnclosingNonVirtualFunctionInitializeParameter(init, f) and
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
call.getThisArgument() = instr and
init.getIRVariable() instanceof IRThisVariable
}
/** Holds if `f` is the enclosing non-virtual function of `init`. */
private predicate isEnclosingNonVirtualFunctionInitializeParameter(
private predicate getEnclosingNonVirtualFunctionInitializeParameter(
InitializeParameterInstruction init, Function f
) {
not f.isVirtual() and
@@ -207,7 +145,7 @@ private module Cached {
}
/** Holds if `f` is the enclosing non-virtual function of `init`. */
private predicate isEnclosingNonVirtualFunctionInitializeIndirection(
private predicate getEnclosingNonVirtualFunctionInitializeIndirection(
InitializeIndirectionInstruction init, Function f
) {
not f.isVirtual() and
@@ -215,16 +153,15 @@ private module Cached {
}
/**
* Holds if `argument` is an argument (or argument indirection) to a call, and
* `parameter` is the corresponding initialization instruction in the call target.
* Holds if `instr` is an argument (or argument indirection) to a call, and
* `succ` is the corresponding initialization instruction in the call target.
*/
cached
predicate flowThroughCallable(Instruction argument, Instruction parameter) {
private predicate flowThroughCallable(Instruction argument, Instruction parameter) {
// Flow from an argument to a parameter
exists(CallInstruction call, InitializeParameterInstruction init | init = parameter |
isPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget())
getPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget())
or
isThisArgumentInitParam(call, argument, init, call.getStaticCallTarget())
getThisArgumentInitParam(call, argument, init, call.getStaticCallTarget())
)
or
// Flow from argument indirection to parameter indirection
@@ -233,7 +170,7 @@ private module Cached {
|
init = parameter and
read.getPrimaryInstruction() = call and
isEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
exists(int n |
read.getSideEffectOperand().getAnyDef() = argument and
@@ -268,10 +205,92 @@ private module Cached {
}
cached
predicate localStep(Instruction nodeFrom, Instruction nodeTo) {
predicate step(Instruction nodeFrom, Instruction nodeTo) {
exists(Operand mid |
instructionToOperandStep(nodeFrom, mid) and
operandToInstructionStep(mid, nodeTo)
)
or
flowThroughCallable(nodeFrom, nodeTo)
}
}
/**
* Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this
* predicate ensures that joins go from `n` to the result instead of the other
* way around.
*/
pragma[inline]
private IRFunction getEnclosingCallable(Instruction n) {
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction()
}
/** Holds if `nodeFrom` flows to `nodeTo`. */
private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) {
exists(config) and
Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
(
config.allowInterproceduralFlow()
or
getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
)
or
config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
}
private newtype TLocalPathNode =
MkLocalPathNode(Instruction n, MustFlowConfiguration config) {
flowsToSink(n, config) and
(
config.isSource(n)
or
exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config))
)
}
/** A `Node` that is in a path from a source to a sink. */
class MustFlowPathNode extends TLocalPathNode {
Instruction n;
MustFlowPathNode() { this = MkLocalPathNode(n, _) }
/** Gets the underlying node. */
Instruction getInstruction() { result = n }
/** Gets a textual representation of this node. */
string toString() { result = n.getAst().toString() }
/** Gets the location of this element. */
Location getLocation() { result = n.getLocation() }
/** Gets a successor node, if any. */
MustFlowPathNode getASuccessor() {
step(this.getInstruction(), result.getInstruction(), this.getConfiguration())
}
/** Gets the associated configuration. */
MustFlowConfiguration getConfiguration() { this = MkLocalPathNode(_, result) }
}
private class MustFlowPathSink extends MustFlowPathNode {
MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) }
}
/**
* Provides the query predicates needed to include a graph in a path-problem query.
*/
module PathGraph {
private predicate reach(MustFlowPathNode n) {
n instanceof MustFlowPathSink or reach(n.getASuccessor())
}
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(MustFlowPathNode a, MustFlowPathNode b) {
a.getASuccessor() = b and reach(b)
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(MustFlowPathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
}

View File

@@ -238,12 +238,7 @@ private module TrackVirtualDispatch<methodDispatchSig/1 virtualDispatch0> {
private import TypeTracking<Location, TtInput>::TypeTrack<qualifierSource/1>::Graph<qualifierOfVirtualCall/1>
private predicate isSource(PathNode n) { n.isSource() }
private predicate isSink(PathNode n) { n.isSink() }
private predicate edgePlus(PathNode n1, PathNode n2) =
doublyBoundedFastTC(edges/2, isSource/1, isSink/1)(n1, n2)
private predicate edgePlus(PathNode n1, PathNode n2) = fastTC(edges/2)(n1, n2)
/**
* Gets the most specific implementation of `mf` that may be called when the
@@ -260,15 +255,6 @@ private module TrackVirtualDispatch<methodDispatchSig/1 virtualDispatch0> {
)
}
pragma[nomagic]
private MemberFunction mostSpecificForSource(PathNode p1, MemberFunction mf) {
p1.isSource() and
exists(Class derived |
qualifierSourceImpl(p1.getNode(), derived) and
result = mostSpecific(mf, derived)
)
}
/**
* Gets a possible pair of end-points `(p1, p2)` where:
* - `p1` is a derived-to-base conversion that converts from some
@@ -278,16 +264,16 @@ private module TrackVirtualDispatch<methodDispatchSig/1 virtualDispatch0> {
* - `callable` is the most specific implementation that may be called when
* the qualifier has type `derived`.
*/
bindingset[p1, p2]
pragma[inline_late]
private predicate pairCand(
PathNode p1, PathNode p2, DataFlowPrivate::DataFlowCallable callable,
DataFlowPrivate::DataFlowCall call
) {
p2.isSink() and
exists(MemberFunction mf |
exists(Class derived, MemberFunction mf |
qualifierSourceImpl(p1.getNode(), derived) and
qualifierOfVirtualCallImpl(p2.getNode(), call.asCallInstruction(), mf) and
callable.asSourceCallable() = mostSpecificForSource(p1, mf)
p1.isSource() and
p2.isSink() and
callable.asSourceCallable() = mostSpecific(mf, derived)
)
}

View File

@@ -1,6 +1,5 @@
private import cpp as Cpp
private import DataFlowUtil
private import DataFlowNodes
private import semmle.code.cpp.ir.IR
private import DataFlowDispatch
private import semmle.code.cpp.ir.internal.IRCppLanguage
@@ -17,42 +16,28 @@ private import semmle.code.cpp.dataflow.ExternalFlow as External
cached
private module Cached {
cached
newtype TIRDataFlowNode0 =
TInstructionNode0(Instruction i) {
not Ssa::ignoreInstruction(i) and
not exists(Operand op |
not Ssa::ignoreOperand(op) and i = Ssa::getIRRepresentationOfOperand(op)
) and
// We exclude `void`-typed instructions because they cannot contain data.
// However, if the instruction is a glvalue, and their type is `void`, then the result
// type of the instruction is really `void*`, and thus we still want to have a dataflow
// node for it.
(not i.getResultType() instanceof VoidType or i.isGLValue())
} or
TMultipleUseOperandNode0(Operand op) {
not Ssa::ignoreOperand(op) and not exists(Ssa::getIRRepresentationOfOperand(op))
} or
TSingleUseOperandNode0(Operand op) {
not Ssa::ignoreOperand(op) and exists(Ssa::getIRRepresentationOfOperand(op))
}
cached
string toStringCached(Node n) {
result = toExprString(n)
or
not exists(toExprString(n)) and
result = n.toStringImpl()
module Nodes0 {
cached
newtype TIRDataFlowNode0 =
TInstructionNode0(Instruction i) {
not Ssa::ignoreInstruction(i) and
not exists(Operand op |
not Ssa::ignoreOperand(op) and i = Ssa::getIRRepresentationOfOperand(op)
) and
// We exclude `void`-typed instructions because they cannot contain data.
// However, if the instruction is a glvalue, and their type is `void`, then the result
// type of the instruction is really `void*`, and thus we still want to have a dataflow
// node for it.
(not i.getResultType() instanceof VoidType or i.isGLValue())
} or
TMultipleUseOperandNode0(Operand op) {
not Ssa::ignoreOperand(op) and not exists(Ssa::getIRRepresentationOfOperand(op))
} or
TSingleUseOperandNode0(Operand op) {
not Ssa::ignoreOperand(op) and exists(Ssa::getIRRepresentationOfOperand(op))
}
}
cached
Location getLocationCached(Node n) { result = n.getLocationImpl() }
cached
newtype TContentApprox =
TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or
TUnionApproxContent(string s) { unionHasApproxName(_, s) } or
TElementApproxContent()
/**
* Gets an additional term that is added to the `join` and `branch` computations to reflect
* an additional forward or backwards branching factor that is not taken into account
@@ -74,174 +59,38 @@ private module Cached {
result = countNumberOfBranchesUsingParameter(switch, p)
)
}
cached
newtype TDataFlowCallable =
TSourceCallable(Cpp::Declaration decl) or
TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c)
cached
newtype TDataFlowCall =
TNormalCall(CallInstruction call) or
TSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
/**
* Holds if data can flow from `node1` to `node2` in a way that loses the
* calling context. For example, this would happen with flow through a
* global or static variable.
*/
cached
predicate jumpStep(Node n1, Node n2) {
exists(GlobalLikeVariable v |
exists(Ssa::GlobalUse globalUse |
v = globalUse.getVariable() and
n1.(FinalGlobalValue).getGlobalUse() = globalUse
|
globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and
v = n2.asVariable()
or
v = n2.asIndirectVariable(globalUse.getIndirection())
)
or
exists(Ssa::GlobalDef globalDef |
v = globalDef.getVariable() and
n2.(InitialGlobalValue).getGlobalDef() = globalDef
|
globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and
v = n1.asVariable()
or
v = n1.asIndirectVariable(globalDef.getIndirection())
)
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(),
n2.(FlowSummaryNode).getSummaryNode())
}
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*
* The boolean `certain` is true if the destination address does not involve
* any pointer arithmetic, and false otherwise.
*/
cached
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
exists(
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
StoreInstruction store, FieldContent fc
|
postFieldUpdate = node2 and
fc = c and
nodeHasInstruction(node1, pragma[only_bind_into](store),
pragma[only_bind_into](indirectionIndex1)) and
postFieldUpdate.getIndirectionIndex() = 1 and
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
store.getDestinationAddressOperand(), numberOfLoads, certain) and
fc.getAField() = postFieldUpdate.getUpdatedField() and
getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode()) and
certain = true
}
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*/
cached
predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) }
/**
* Holds if data can flow from `node1` to `node2` via a read of `f`.
* Thus, `node1` references an object with a field `f` whose value ends up in
* `node2`.
*/
cached
predicate readStep(Node node1, ContentSet c, Node node2) {
exists(
FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc
|
fc = c and
nodeHasOperand(node2, operand, indirectionIndex2) and
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
// in `storeStep`.
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
fc.getAField() = fa1.getField() and
getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
}
/**
* Holds if values stored inside content `c` are cleared at node `n`.
*/
cached
predicate clearsContent(Node n, ContentSet c) {
n =
any(PostUpdateNode pun, Content d |
d.impliesClearOf(c) and storeStepImpl(_, d, pun, true)
|
pun
).getPreUpdateNode() and
(
not exists(Operand op, Cpp::Operation p |
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
(
p instanceof Cpp::AssignPointerAddExpr or
p instanceof Cpp::AssignPointerSubExpr or
p instanceof Cpp::CrementOperation
)
|
p.getAnOperand() = op.getUse().getAst()
)
or
forex(PostUpdateNode pun, Content d |
pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and
storeStepImpl(_, d, pun, true) and
pun.getPreUpdateNode() = n
|
c.(Content).getIndirectionIndex() = d.getIndirectionIndex()
)
)
}
}
import Cached
private int getNumberOfIndirections(Node n) {
result = n.(RawIndirectOperand).getIndirectionIndex()
or
result = n.(RawIndirectInstruction).getIndirectionIndex()
or
result = n.(VariableNode).getIndirectionIndex()
or
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
or
result = n.(FinalParameterNode).getIndirectionIndex()
or
result = n.(BodyLessParameterNodeImpl).getIndirectionIndex()
}
private import Nodes0
/**
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
* output for `n`.
* A module for calculating the number of stars (i.e., `*`s) needed for various
* dataflow node `toString` predicates.
*/
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
module NodeStars {
private int getNumberOfIndirections(Node n) {
result = n.(RawIndirectOperand).getIndirectionIndex()
or
result = n.(RawIndirectInstruction).getIndirectionIndex()
or
result = n.(VariableNode).getIndirectionIndex()
or
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
or
result = n.(FinalParameterNode).getIndirectionIndex()
or
result = n.(BodyLessParameterNodeImpl).getIndirectionIndex()
}
/**
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
* output for `n`.
*/
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
}
import NodeStars
/**
* A cut-down `DataFlow::Node` class that does not depend on the output of SSA.
@@ -979,10 +828,85 @@ private int getMinIndirectionForGlobalDef(Ssa::GlobalDef def) {
result = getMinIndirectionsForType(def.getUnspecifiedType())
}
/**
* Holds if data can flow from `node1` to `node2` in a way that loses the
* calling context. For example, this would happen with flow through a
* global or static variable.
*/
predicate jumpStep(Node n1, Node n2) {
exists(GlobalLikeVariable v |
exists(Ssa::GlobalUse globalUse |
v = globalUse.getVariable() and
n1.(FinalGlobalValue).getGlobalUse() = globalUse
|
globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and
v = n2.asVariable()
or
v = n2.asIndirectVariable(globalUse.getIndirection())
)
or
exists(Ssa::GlobalDef globalDef |
v = globalDef.getVariable() and
n2.(InitialGlobalValue).getGlobalDef() = globalDef
|
globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and
v = n1.asVariable()
or
v = n1.asIndirectVariable(globalDef.getIndirection())
)
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(),
n2.(FlowSummaryNode).getSummaryNode())
}
bindingset[c]
pragma[inline_late]
private int getIndirectionIndexLate(Content c) { result = c.getIndirectionIndex() }
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*
* The boolean `certain` is true if the destination address does not involve
* any pointer arithmetic, and false otherwise. This has to do with whether a
* store step can be used to clear a field (see `clearsContent`).
*/
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
exists(
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
StoreInstruction store, FieldContent fc
|
postFieldUpdate = node2 and
fc = c and
nodeHasInstruction(node1, pragma[only_bind_into](store),
pragma[only_bind_into](indirectionIndex1)) and
postFieldUpdate.getIndirectionIndex() = 1 and
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
store.getDestinationAddressOperand(), numberOfLoads, certain) and
fc.getAField() = postFieldUpdate.getUpdatedField() and
getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode()) and
certain = true
}
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*/
predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) }
/**
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
* operations and exactly `n` `LoadInstruction` operations.
*/
private predicate numberOfLoadsFromOperandRec(
Operand operandFrom, Operand operandTo, int ind, boolean certain
) {
@@ -1033,6 +957,63 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex)
hasInstructionAndIndex(node, instr, indirectionIndex)
}
/**
* Holds if data can flow from `node1` to `node2` via a read of `f`.
* Thus, `node1` references an object with a field `f` whose value ends up in
* `node2`.
*/
predicate readStep(Node node1, ContentSet c, Node node2) {
exists(
FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc
|
fc = c and
nodeHasOperand(node2, operand, indirectionIndex2) and
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
// in `storeStep`.
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
fc.getAField() = fa1.getField() and
getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
}
/**
* Holds if values stored inside content `c` are cleared at node `n`.
*/
predicate clearsContent(Node n, ContentSet c) {
n =
any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun)
.getPreUpdateNode() and
(
// The crement operations and pointer addition and subtraction self-assign. We do not
// want to clear the contents if it is indirectly pointed at by any of these operations,
// as part of the contents might still be accessible afterwards. If there is no such
// indirection clearing the contents is safe.
not exists(Operand op, Cpp::Operation p |
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
(
p instanceof Cpp::AssignPointerAddExpr or
p instanceof Cpp::AssignPointerSubExpr or
p instanceof Cpp::CrementOperation
)
|
p.getAnOperand() = op.getUse().getAst()
)
or
forex(PostUpdateNode pun, Content d |
pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and
storeStepImpl(_, d, pun, true) and
pun.getPreUpdateNode() = n
|
c.(Content).getIndirectionIndex() = d.getIndirectionIndex()
)
)
}
/**
* Holds if the value that is being tracked is expected to be stored inside content `c`
* at node `n`.
@@ -1065,6 +1046,11 @@ class CastNode extends Node {
CastNode() { none() } // stub implementation
}
cached
private newtype TDataFlowCallable =
TSourceCallable(Cpp::Declaration decl) or
TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c)
/**
* A callable, which may be:
* - a function (that may contain code)
@@ -1148,6 +1134,15 @@ class DataFlowType extends TypeFinal {
string toString() { result = "" }
}
cached
private newtype TDataFlowCall =
TNormalCall(CallInstruction call) or
TSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
private predicate summarizedCallableIsManual(SummarizedCallable sc) {
sc.asSummarizedCallable().hasManualModel()
}
@@ -1170,7 +1165,7 @@ class DataFlowCall extends TDataFlowCall {
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
Declaration getStaticCallSourceTarget() { none() }
Function getStaticCallSourceTarget() { none() }
/**
* Gets the target of this call. We use the following strategy for deciding
@@ -1182,7 +1177,7 @@ class DataFlowCall extends TDataFlowCall {
* whether is it manual or generated.
*/
final DataFlowCallable getStaticCallTarget() {
exists(Declaration target | target = this.getStaticCallSourceTarget() |
exists(Function target | target = this.getStaticCallSourceTarget() |
// Don't use the source callable if there is a manual model for the
// target
not exists(SummarizedCallable sc |
@@ -1242,7 +1237,7 @@ private class NormalCall extends DataFlowCall, TNormalCall {
override CallTargetOperand getCallTargetOperand() { result = call.getCallTargetOperand() }
override Declaration getStaticCallSourceTarget() { result = call.getStaticCallTarget() }
override Function getStaticCallSourceTarget() { result = call.getStaticCallTarget() }
override ArgumentOperand getArgumentOperand(int index) { result = call.getArgumentOperand(index) }
@@ -1528,6 +1523,12 @@ private predicate fieldHasApproxName(Field f, string s) {
private predicate unionHasApproxName(Cpp::Union u, string s) { s = u.getName().charAt(0) }
cached
private newtype TContentApprox =
TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or
TUnionApproxContent(string s) { unionHasApproxName(_, s) } or
TElementApproxContent()
/** An approximated `Content`. */
class ContentApprox extends TContentApprox {
string toString() { none() } // overridden in subclasses

View File

@@ -6,8 +6,8 @@ private import cpp
private import semmle.code.cpp.ir.IR
private import DataFlowUtil
private import DataFlowPrivate
private import DataFlowNodes
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
cached
private module Cached {
@@ -73,9 +73,17 @@ private module Cached {
// a result for `getConvertedResultExpression`. We remap this here so that
// this `ConvertInstruction` maps to the result of the expression that
// represents the extent.
result = IRConstruction::Raw::getAllocationExtentConvertExpr(instr)
exists(TranslatedNonConstantAllocationSize tas |
result = tas.getExtent().getExpr() and
instr = tas.getInstruction(AllocationExtentConvertTag())
)
or
result = IRConstruction::Raw::getTransparentConversionParenthesisExpr(instr)
// There's no instruction that returns `ParenthesisExpr`, but some queries
// expect this
exists(TranslatedTransparentConversion ttc |
result = ttc.getExpr().(ParenthesisExpr) and
instr = ttc.getResult()
)
or
// Certain expressions generate `CopyValueInstruction`s only when they
// are needed. Examples of this include crement operations and compound
@@ -104,10 +112,10 @@ private module Cached {
// needed, and in that case the only value that will propagate forward in
// the program is the value that's been updated. So in those cases we just
// use the result of `node.asDefinition()` as the result of `node.asExpr()`.
exists(StoreInstruction store |
store = instr and
IRConstruction::Raw::instructionProducesExprResult(store) and
result = asDefinitionImpl0(store)
exists(TranslatedCoreExpr tco |
tco.getInstruction(_) = instr and
tco.producesExprResult() and
result = asDefinitionImpl0(instr)
)
or
// IR construction breaks an array aggregate literal `{1, 2, 3}` into a
@@ -137,9 +145,18 @@ private module Cached {
// For an expression such as `i += 2` we pretend that the generated
// `StoreInstruction` contains the result of the expression even though
// this isn't totally aligned with the C/C++ standard.
result = IRConstruction::Raw::getAssignOperationStoreExpr(store)
exists(TranslatedAssignOperation tao |
store = tao.getInstruction(AssignmentStoreTag()) and
result = tao.getExpr()
)
or
result = IRConstruction::Raw::getCrementOperationStoreExpr(store)
// Similarly for `i++` and `++i` we pretend that the generated
// `StoreInstruction` contains the result of the expression even though
// this isn't totally aligned with the C/C++ standard.
exists(TranslatedCrementOperation tco |
store = tco.getInstruction(CrementStoreTag()) and
result = tco.getExpr()
)
}
/**
@@ -149,7 +166,11 @@ private module Cached {
*/
private predicate excludeAsDefinitionResult(StoreInstruction store) {
// Exclude the store to the temporary generated by a ternary expression.
IRConstruction::Raw::isConditionalExprTempStore(store)
exists(TranslatedConditionalExpr tce |
store = tce.getInstruction(ConditionValueFalseStoreTag())
or
store = tce.getInstruction(ConditionValueTrueStoreTag())
)
}
/**

View File

@@ -6,7 +6,6 @@
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
private import DataFlowUtil
private import DataFlowNodes
private import DataFlowPrivate
private import SsaImpl as Ssa

View File

@@ -6,7 +6,6 @@ private import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
private import PrintIRUtilities
/** A property provider for local IR dataflow store steps. */

View File

@@ -2,7 +2,6 @@ private import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
private import SsaImpl as Ssa
private import PrintIRUtilities

View File

@@ -6,7 +6,6 @@ private import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
private Instruction getInstruction(Node n, string stars) {
result = [n.asInstruction(), n.(RawIndirectInstruction).getInstruction()] and

View File

@@ -10,9 +10,8 @@ private import semmle.code.cpp.models.interfaces.PartialFlow as PartialFlow
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as FIO
private import semmle.code.cpp.ir.internal.IRCppLanguage
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedInitialization
private import DataFlowPrivate
private import DataFlowNodes
import SsaImplCommon
private module SourceVariables {
@@ -439,7 +438,10 @@ private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVari
* initialize `v`.
*/
private Instruction getInitializationTargetAddress(IRVariable v) {
result = IRConstruction::Raw::getInitializationTargetAddress(v)
exists(TranslatedVariableInitialization init |
init.getIRVariable() = v and
result = init.getTargetAddress()
)
}
/** An initial definition of an SSA variable address. */

View File

@@ -4,25 +4,75 @@ import semmle.code.cpp.ir.internal.IRCppLanguage
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
private import DataFlowImplCommon as DataFlowImplCommon
private import DataFlowUtil
private import DataFlowNodes
private import semmle.code.cpp.models.interfaces.PointerWrapper
private import DataFlowPrivate
private import TypeFlow
private import semmle.code.cpp.ir.ValueNumbering
/**
* Gets the C++ type of `this` in an `IRFunction` generated from `f`.
* Holds if `operand` is an operand that is not used by the dataflow library.
* Ignored operands are not recognized as uses by SSA, and they don't have a
* corresponding `(Indirect)OperandNode`.
*/
predicate ignoreOperand(Operand operand) {
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
operand instanceof MemoryOperand
}
/**
* Holds if `instr` is an instruction that is not used by the dataflow library.
* Ignored instructions are not recognized as reads/writes by SSA, and they
* don't have a corresponding `(Indirect)InstructionNode`.
*/
predicate ignoreInstruction(Instruction instr) {
DataFlowImplCommon::forceCachingInSameStage() and
(
instr instanceof CallSideEffectInstruction or
instr instanceof CallReadSideEffectInstruction or
instr instanceof ExitFunctionInstruction or
instr instanceof EnterFunctionInstruction or
instr instanceof WriteSideEffectInstruction or
instr instanceof PhiInstruction or
instr instanceof ReadSideEffectInstruction or
instr instanceof ChiInstruction or
instr instanceof InitializeIndirectionInstruction or
instr instanceof AliasedDefinitionInstruction or
instr instanceof AliasedUseInstruction or
instr instanceof InitializeNonLocalInstruction or
instr instanceof ReturnIndirectionInstruction or
instr instanceof UninitializedGroupInstruction
)
}
/**
* Gets the C++ type of `this` in the member function `f`.
* The result is a glvalue if `isGLValue` is true, and
* a prvalue if `isGLValue` is false.
*/
bindingset[isGLValue]
private CppType getThisType(Cpp::Declaration f, boolean isGLValue) {
result.hasType(f.(Cpp::MemberFunction).getTypeOfThis(), isGLValue)
or
exists(Cpp::PointerType pt |
pt.getBaseType() = f.(Cpp::Field).getDeclaringType() and
result.hasType(pt, isGLValue)
)
private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) {
result.hasType(f.getTypeOfThis(), isGLValue)
}
/**
* Gets the C++ type of the instruction `i`.
*
* This is equivalent to `i.getResultLanguageType()` with the exception
* of instructions that directly references a `this` IRVariable. In this
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
* predicate gives the expected type (i.e., a potentially cv-qualified
* type `A*` where `A` is the declaring type of the member function that
* contains `i`).
*/
cached
CppType getResultLanguageType(Instruction i) {
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
then
if i.isGLValue()
then result = getThisType(i.getEnclosingFunction(), true)
else result = getThisType(i.getEnclosingFunction(), false)
else result = i.getResultLanguageType()
}
/**
@@ -180,8 +230,7 @@ private class PointerWrapperTypeIndirection extends Indirection instanceof Point
override predicate isAdditionalDereference(Instruction deref, Operand address) {
exists(CallInstruction call |
operandForFullyConvertedCall(getAUse(deref), call) and
this =
call.getStaticCallTarget().(Function).getClassAndName(["operator*", "operator->", "get"]) and
this = call.getStaticCallTarget().getClassAndName(["operator*", "operator->", "get"]) and
address = call.getThisArgumentOperand()
)
}
@@ -200,7 +249,7 @@ private module IteratorIndirections {
override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) {
exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() |
this = call.getStaticCallTarget().(Function).getClassAndName("operator=") and
this = call.getStaticCallTarget().getClassAndName("operator=") and
address = call.getThisArgumentOperand() and
certain = false
)
@@ -298,6 +347,10 @@ predicate isWrite(Node0Impl value, Operand address, boolean certain) {
)
}
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
}
newtype TBaseSourceVariable =
// Each IR variable gets its own source variable
TBaseIRVariable(IRVariable var) or
@@ -519,69 +572,6 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns
cached
private module Cached {
/**
* Holds if `operand` is an operand that is not used by the dataflow library.
* Ignored operands are not recognized as uses by SSA, and they don't have a
* corresponding `(Indirect)OperandNode`.
*/
cached
predicate ignoreOperand(Operand operand) {
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
operand instanceof MemoryOperand
}
/**
* Holds if `instr` is an instruction that is not used by the dataflow library.
* Ignored instructions are not recognized as reads/writes by SSA, and they
* don't have a corresponding `(Indirect)InstructionNode`.
*/
cached
predicate ignoreInstruction(Instruction instr) {
DataFlowImplCommon::forceCachingInSameStage() and
(
instr instanceof CallSideEffectInstruction or
instr instanceof CallReadSideEffectInstruction or
instr instanceof ExitFunctionInstruction or
instr instanceof EnterFunctionInstruction or
instr instanceof WriteSideEffectInstruction or
instr instanceof PhiInstruction or
instr instanceof ReadSideEffectInstruction or
instr instanceof ChiInstruction or
instr instanceof InitializeIndirectionInstruction or
instr instanceof AliasedDefinitionInstruction or
instr instanceof AliasedUseInstruction or
instr instanceof InitializeNonLocalInstruction or
instr instanceof ReturnIndirectionInstruction or
instr instanceof UninitializedGroupInstruction
)
}
cached
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
}
/**
* Gets the C++ type of the instruction `i`.
*
* This is equivalent to `i.getResultLanguageType()` with the exception
* of instructions that directly references a `this` IRVariable. In this
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
* predicate gives the expected type (i.e., a potentially cv-qualified
* type `A*` where `A` is the declaring type of the member function that
* contains `i`).
*/
cached
CppType getResultLanguageType(Instruction i) {
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
then
if i.isGLValue()
then result = getThisType(i.getEnclosingFunction(), true)
else result = getThisType(i.getEnclosingFunction(), false)
else result = i.getResultLanguageType()
}
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
private predicate isConversion(Operand op) {
exists(Instruction def, Operand use |

View File

@@ -5,81 +5,64 @@ private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.models.interfaces.SideEffect
private import DataFlowUtil
private import DataFlowPrivate
private import DataFlowNodes
private import SsaImpl as Ssa
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.cpp.ir.dataflow.FlowSteps
cached
private module Cached {
private import DataFlowImplCommon as DataFlowImplCommon
/**
* This predicate exists to collapse the `cached` predicates in this module with the
* `cached` predicates in other C/C++ dataflow files, which is then collapsed
* with the `cached` predicates in `DataFlowImplCommon.qll`.
*/
cached
predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() }
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step. This relation is only used for local taint flow
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
* special cases that should only apply to local taint flow.
*/
cached
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// dataflow step
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
// taint flow step
localAdditionalTaintStep(nodeFrom, nodeTo, _)
or
// models-as-data summarized flow for local data flow (i.e. special case for flow
// through calls to modeled functions, without relying on global dataflow to join
// the dots).
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
}
/**
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
* different objects.
*/
cached
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
model = ""
or
modeledTaintStep(nodeFrom, nodeTo, model)
or
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
// indirection of the pointer arithmetic instruction. This provides flow from `source`
// in `x[source]` to the result of the associated load instruction.
exists(PointerArithmeticInstruction pai, int indirectionIndex |
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
) and
model = ""
or
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
model = ""
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
or
// object->field conflation for content that is a `TaintInheritingContent`.
exists(DataFlow::ContentSet f |
readStep(nodeFrom, f, nodeTo) and
f.getAReadContent() instanceof TaintInheritingContent
) and
model = ""
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step. This relation is only used for local taint flow
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
* special cases that should only apply to local taint flow.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// dataflow step
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
// taint flow step
localAdditionalTaintStep(nodeFrom, nodeTo, _)
or
// models-as-data summarized flow for local data flow (i.e. special case for flow
// through calls to modeled functions, without relying on global dataflow to join
// the dots).
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
}
import Cached
/**
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
* different objects.
*/
cached
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
model = ""
or
modeledTaintStep(nodeFrom, nodeTo, model)
or
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
// indirection of the pointer arithmetic instruction. This provides flow from `source`
// in `x[source]` to the result of the associated load instruction.
exists(PointerArithmeticInstruction pai, int indirectionIndex |
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
) and
model = ""
or
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
model = ""
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
or
// object->field conflation for content that is a `TaintInheritingContent`.
exists(DataFlow::ContentSet f |
readStep(nodeFrom, f, nodeTo) and
f.getAReadContent() instanceof TaintInheritingContent
) and
model = ""
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
@@ -213,7 +196,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
// to that output, but the deref is not modeled in the IR for the caller.
exists(
CallInstruction call, SideEffectOperandNode indirectArgument, Function func,
CallInstruction call, DataFlow::SideEffectOperandNode indirectArgument, Function func,
FunctionInput modelIn, FunctionOutput modelOut
|
indirectArgument = callInput(call, modelIn) and

View File

@@ -495,7 +495,7 @@ class FieldInstruction extends Instruction {
* `FunctionAddress` instruction.
*/
class FunctionInstruction extends Instruction {
Language::Declaration funcSymbol;
Language::Function funcSymbol;
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
@@ -504,7 +504,7 @@ class FunctionInstruction extends Instruction {
/**
* Gets the function that this instruction references.
*/
final Language::Declaration getFunctionSymbol() { result = funcSymbol }
final Language::Function getFunctionSymbol() { result = funcSymbol }
}
/**
@@ -1678,7 +1678,7 @@ class CallInstruction extends Instruction {
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
final Language::Declaration getStaticCallTarget() {
final Language::Function getStaticCallTarget() {
result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol()
}

View File

@@ -495,7 +495,7 @@ class FieldInstruction extends Instruction {
* `FunctionAddress` instruction.
*/
class FunctionInstruction extends Instruction {
Language::Declaration funcSymbol;
Language::Function funcSymbol;
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
@@ -504,7 +504,7 @@ class FunctionInstruction extends Instruction {
/**
* Gets the function that this instruction references.
*/
final Language::Declaration getFunctionSymbol() { result = funcSymbol }
final Language::Function getFunctionSymbol() { result = funcSymbol }
}
/**
@@ -1678,7 +1678,7 @@ class CallInstruction extends Instruction {
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
final Language::Declaration getStaticCallTarget() {
final Language::Function getStaticCallTarget() {
result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol()
}

View File

@@ -15,8 +15,6 @@ private import TranslatedCall
private import TranslatedStmt
private import TranslatedFunction
private import TranslatedGlobalVar
private import TranslatedNonStaticDataMember
private import TranslatedInitialization
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
instruction = TRawInstruction(result, _)
@@ -46,9 +44,6 @@ module Raw {
or
not var.isFromUninstantiatedTemplate(_) and
var instanceof StaticInitializedStaticLocalVariable
or
not var.isFromUninstantiatedTemplate(_) and
var instanceof Field
) and
var.hasInitializer() and
(
@@ -68,8 +63,6 @@ module Raw {
getTranslatedFunction(decl).hasUserVariable(var, type)
or
getTranslatedVarInit(decl).hasUserVariable(var, type)
or
getTranslatedFieldInit(decl).hasUserVariable(var, type)
}
cached
@@ -116,7 +109,7 @@ module Raw {
}
cached
Declaration getInstructionFunction(Instruction instruction) {
Function getInstructionFunction(Instruction instruction) {
result =
getInstructionTranslatedElement(instruction)
.getInstructionFunction(getInstructionTag(instruction))
@@ -201,89 +194,6 @@ module Raw {
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
}
/**
* Gets the expression associated with the instruction `instr` that computes
* the `Convert` instruction on the extent expression of an allocation.
*/
cached
Expr getAllocationExtentConvertExpr(Instruction instr) {
exists(TranslatedNonConstantAllocationSize tas |
instr = tas.getInstruction(AllocationExtentConvertTag()) and
result = tas.getExtent().getExpr()
)
}
/**
* Gets the `ParenthesisExpr` associated with a transparent conversion
* instruction, if any.
*/
cached
ParenthesisExpr getTransparentConversionParenthesisExpr(Instruction instr) {
exists(TranslatedTransparentConversion ttc |
result = ttc.getExpr() and
instr = ttc.getResult()
)
}
/**
* Holds if `instr` belongs to a `TranslatedCoreExpr` that produces an
* expression result. This indicates that the instruction represents a
* definition whose result should be mapped back to the expression.
*/
cached
predicate instructionProducesExprResult(Instruction instr) {
exists(TranslatedCoreExpr tco |
tco.getInstruction(_) = instr and
tco.producesExprResult()
)
}
/**
* Gets the expression associated with a `StoreInstruction` generated
* by an `TranslatedAssignOperation`.
*/
cached
Expr getAssignOperationStoreExpr(StoreInstruction store) {
exists(TranslatedAssignOperation tao |
store = tao.getInstruction(AssignmentStoreTag()) and
result = tao.getExpr()
)
}
/**
* Gets the expression associated with a `StoreInstruction` generated
* by an `TranslatedCrementOperation`.
*/
cached
Expr getCrementOperationStoreExpr(StoreInstruction store) {
exists(TranslatedCrementOperation tco |
store = tco.getInstruction(CrementStoreTag()) and
result = tco.getExpr()
)
}
/**
* Holds if `store` is a `StoreInstruction` that defines the temporary
* `IRVariable` generated as part of the translation of a ternary expression.
*/
cached
predicate isConditionalExprTempStore(StoreInstruction store) {
exists(TranslatedConditionalExpr tce |
store = tce.getInstruction(ConditionValueFalseStoreTag())
or
store = tce.getInstruction(ConditionValueTrueStoreTag())
)
}
/** Gets the instruction that computes the address used to initialize `v`. */
cached
Instruction getInitializationTargetAddress(IRVariable v) {
exists(TranslatedVariableInitialization init |
init.getIRVariable() = v and
result = init.getTargetAddress()
)
}
}
class TStageInstruction = TRawInstruction or TRawUnreachedInstruction;

View File

@@ -130,31 +130,27 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
}
/**
* An expression that can have call side effects.
* A `Call` or `NewOrNewArrayExpr` or `DeleteOrDeleteArrayExpr`.
*
* All kinds of expressions invoke a function as part of their evaluation. This class provides a
* way to treat those expressions similarly, and to get the invoked `Declaration`.
* All kinds of expression invoke a function as part of their evaluation. This class provides a
* way to treat both kinds of function similarly, and to get the invoked `Function`.
*/
class ExprWithCallSideEffects extends Expr {
ExprWithCallSideEffects() {
class CallOrAllocationExpr extends Expr {
CallOrAllocationExpr() {
this instanceof Call
or
this instanceof NewOrNewArrayExpr
or
this instanceof DeleteOrDeleteArrayExpr
or
this instanceof ConstructorDefaultFieldInit
}
/** Gets the `Declaration` invoked by this expression, if known. */
final Declaration getTarget() {
/** Gets the `Function` invoked by this expression, if known. */
final Function getTarget() {
result = this.(Call).getTarget()
or
result = this.(NewOrNewArrayExpr).getAllocator()
or
result = this.(DeleteOrDeleteArrayExpr).getDeallocator()
or
result = this.(ConstructorDefaultFieldInit).getTarget()
}
}
@@ -162,7 +158,7 @@ class ExprWithCallSideEffects extends Expr {
* Returns the side effect opcode, if any, that represents any side effects not specifically modeled
* by an argument side effect.
*/
Opcode getCallSideEffectOpcode(ExprWithCallSideEffects expr) {
Opcode getCallSideEffectOpcode(CallOrAllocationExpr expr) {
not exists(expr.getTarget().(SideEffectFunction)) and result instanceof Opcode::CallSideEffect
or
exists(SideEffectFunction sideEffectFunction |
@@ -179,7 +175,7 @@ Opcode getCallSideEffectOpcode(ExprWithCallSideEffects expr) {
/**
* Returns a side effect opcode for parameter index `i` of the specified call.
*
* This predicate will yield at most two results: one read side effect, and one write side effect.
* This predicate will return at most two results: one read side effect, and one write side effect.
*/
Opcode getASideEffectOpcode(Call call, ParameterIndex i) {
exists(boolean buffer |
@@ -232,14 +228,3 @@ Opcode getASideEffectOpcode(Call call, ParameterIndex i) {
)
)
}
/**
* Returns a side effect opcode for a default field initialization.
*
* This predicate will yield two results: one read side effect, and one write side effect.
*/
Opcode getDefaultFieldInitSideEffectOpcode() {
result instanceof Opcode::IndirectReadSideEffect
or
result instanceof Opcode::IndirectMayWriteSideEffect
}

View File

@@ -10,7 +10,6 @@ private import SideEffects
private import TranslatedElement
private import TranslatedExpr
private import TranslatedFunction
private import TranslatedInitialization
private import DefaultOptions as DefaultOptions
/**
@@ -349,7 +348,7 @@ class TranslatedExprCall extends TranslatedCallExpr {
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
override FunctionCall expr;
override Declaration getInstructionFunction(InstructionTag tag) {
override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = expr.getTarget()
}
@@ -430,9 +429,6 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
or
expr instanceof DeleteOrDeleteArrayExpr and
result = getTranslatedDeleteOrDeleteArray(expr).getInstruction(CallTag())
or
expr instanceof ConstructorDefaultFieldInit and
result = getTranslatedConstructorFieldInitialization(expr).getInstruction(CallTag())
}
}
@@ -508,25 +504,11 @@ abstract class TranslatedSideEffect extends TranslatedElement {
abstract predicate sideEffectInstruction(Opcode opcode, CppType type);
}
private class CallOrDefaultFieldInit extends Expr {
CallOrDefaultFieldInit() {
this instanceof Call
or
this instanceof ConstructorDefaultFieldInit
}
Declaration getTarget() {
result = this.(Call).getTarget()
or
result = this.(ConstructorDefaultFieldInit).getTarget()
}
}
/**
* The IR translation of a single argument side effect for a call.
*/
abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
CallOrDefaultFieldInit callOrInit;
Call call;
int index;
SideEffectOpcode sideEffectOpcode;
@@ -542,7 +524,7 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
result = "(read side effect for " + this.getArgString() + ")"
}
override Expr getPrimaryExpr() { result = callOrInit }
override Call getPrimaryExpr() { result = call }
override predicate sortOrder(int group, int indexInGroup) {
indexInGroup = index and
@@ -604,10 +586,9 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
tag instanceof OnlyInstructionTag and
operandTag instanceof BufferSizeOperandTag and
result =
getTranslatedExpr(callOrInit
.(Call)
.getArgument(callOrInit.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
.getFullyConverted()).getResult()
getTranslatedExpr(call.getArgument(call.getTarget()
.(SideEffectFunction)
.getParameterSizeIndex(index)).getFullyConverted()).getResult()
}
/** Holds if this side effect is a write side effect, rather than a read side effect. */
@@ -635,7 +616,7 @@ class TranslatedArgumentExprSideEffect extends TranslatedArgumentSideEffect,
Expr arg;
TranslatedArgumentExprSideEffect() {
this = TTranslatedArgumentExprSideEffect(callOrInit, arg, index, sideEffectOpcode)
this = TTranslatedArgumentExprSideEffect(call, arg, index, sideEffectOpcode)
}
final override Locatable getAst() { result = arg }
@@ -659,31 +640,28 @@ class TranslatedArgumentExprSideEffect extends TranslatedArgumentSideEffect,
* The IR translation of an argument side effect for `*this` on a call, where there is no `Expr`
* object that represents the `this` argument.
*
* This applies to constructor calls and default field initializations, as the AST has explicit
* qualifier `Expr`s for all other calls to non-static member functions.
* The applies only to constructor calls, as the AST has exploit qualifier `Expr`s for all other
* calls to non-static member functions.
*/
class TranslatedImplicitThisQualifierSideEffect extends TranslatedArgumentSideEffect,
TTranslatedImplicitThisQualifierSideEffect
class TranslatedStructorQualifierSideEffect extends TranslatedArgumentSideEffect,
TTranslatedStructorQualifierSideEffect
{
TranslatedImplicitThisQualifierSideEffect() {
this = TTranslatedImplicitThisQualifierSideEffect(callOrInit, sideEffectOpcode) and
TranslatedStructorQualifierSideEffect() {
this = TTranslatedStructorQualifierSideEffect(call, sideEffectOpcode) and
index = -1
}
final override Locatable getAst() { result = callOrInit }
final override Locatable getAst() { result = call }
final override Type getIndirectionType() { result = callOrInit.getTarget().getDeclaringType() }
final override Type getIndirectionType() { result = call.getTarget().getDeclaringType() }
final override string getArgString() { result = "this" }
final override Instruction getArgInstruction() {
exists(TranslatedStructorCall structorCall |
structorCall.getExpr() = callOrInit and
structorCall.getExpr() = call and
result = structorCall.getQualifierResult()
)
or
callOrInit instanceof ConstructorDefaultFieldInit and
result = getTranslatedFunction(callOrInit.getEnclosingFunction()).getLoadThisInstruction()
}
}

View File

@@ -36,8 +36,7 @@ abstract class TranslatedCondition extends TranslatedElement {
final override Declaration getFunction() {
result = getEnclosingFunction(expr) or
result = getEnclosingVariable(expr).(GlobalOrNamespaceVariable) or
result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable) or
result = getEnclosingVariable(expr).(Field)
result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable)
}
final Type getResultType() { result = expr.getUnspecifiedType() }

View File

@@ -34,11 +34,8 @@ abstract class TranslatedDeclarationEntry extends TranslatedElement, TTranslated
or
result = entry.getDeclaration().(GlobalOrNamespaceVariable)
or
result = entry.getDeclaration().(Field)
or
not entry.getDeclaration() instanceof StaticInitializedStaticLocalVariable and
not entry.getDeclaration() instanceof GlobalOrNamespaceVariable and
not entry.getDeclaration() instanceof Field and
result = stmt.getEnclosingFunction()
)
}

View File

@@ -767,7 +767,7 @@ newtype TTranslatedElement =
expr = initList.getFieldExpr(field, position).getFullyConverted()
)
or
exists(ConstructorDirectFieldInit init |
exists(ConstructorFieldInit init |
not ignoreExpr(init) and
ast = init and
field = init.getTarget() and
@@ -775,14 +775,6 @@ newtype TTranslatedElement =
position = -1
)
} or
// The initialization of a field via a default member initializer.
TTranslatedDefaultFieldInitialization(Expr ast, Field field) {
exists(ConstructorDefaultFieldInit init |
not ignoreExpr(init) and
ast = init and
field = init.getTarget()
)
} or
// The value initialization of a field due to an omitted member of an
// initializer list.
TTranslatedFieldValueInitialization(Expr ast, Field field) {
@@ -879,7 +871,7 @@ newtype TTranslatedElement =
// The declaration/initialization part of a `ConditionDeclExpr`
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
// The side effects of a `Call`
TTranslatedCallSideEffects(ExprWithCallSideEffects expr) {
TTranslatedCallSideEffects(CallOrAllocationExpr expr) {
not ignoreExpr(expr) and
not ignoreSideEffects(expr)
} or
@@ -918,23 +910,15 @@ newtype TTranslatedElement =
} or
// Constructor calls lack a qualifier (`this`) expression, so we need to handle the side effects
// on `*this` without an `Expr`.
TTranslatedImplicitThisQualifierSideEffect(ExprWithCallSideEffects call, SideEffectOpcode opcode) {
TTranslatedStructorQualifierSideEffect(Call call, SideEffectOpcode opcode) {
not ignoreExpr(call) and
not ignoreSideEffects(call) and
(
call instanceof ConstructorCall and
opcode = getASideEffectOpcode(call, -1)
or
call instanceof ConstructorFieldInit and
opcode = getDefaultFieldInitSideEffectOpcode()
)
call instanceof ConstructorCall and
opcode = getASideEffectOpcode(call, -1)
} or
// The side effect that initializes newly-allocated memory.
TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } or
TTranslatedStaticStorageDurationVarInit(Variable var) {
Raw::varHasIRFunc(var) and not var instanceof Field
} or
TTranslatedNonStaticDataMemberVarInit(Field var) { Raw::varHasIRFunc(var) } or
TTranslatedStaticStorageDurationVarInit(Variable var) { Raw::varHasIRFunc(var) } or
TTranslatedAssertionOperand(MacroInvocation mi, int index) { hasAssertionOperand(mi, index) }
/**
@@ -1195,7 +1179,7 @@ abstract class TranslatedElement extends TTranslatedElement {
* If the instruction specified by `tag` is a `FunctionInstruction`, gets the
* `Function` for that instruction.
*/
Declaration getInstructionFunction(InstructionTag tag) { none() }
Function getInstructionFunction(InstructionTag tag) { none() }
/**
* If the instruction specified by `tag` is a `VariableInstruction`, gets the
@@ -1313,7 +1297,5 @@ abstract class TranslatedRootElement extends TranslatedElement {
this instanceof TTranslatedFunction
or
this instanceof TTranslatedStaticStorageDurationVarInit
or
this instanceof TTranslatedNonStaticDataMemberVarInit
}
}

View File

@@ -14,7 +14,6 @@ private import TranslatedFunction
private import TranslatedInitialization
private import TranslatedStmt
private import TranslatedGlobalVar
private import TranslatedNonStaticDataMember
private import IRConstruction
import TranslatedCall
@@ -139,8 +138,6 @@ abstract class TranslatedExpr extends TranslatedElement {
result = getTranslatedFunction(getEnclosingFunction(expr))
or
result = getTranslatedVarInit(getEnclosingVariable(expr))
or
result = getTranslatedFieldInit(getEnclosingVariable(expr))
}
}
@@ -156,10 +153,7 @@ Declaration getEnclosingDeclaration0(Expr e) {
i.getExpr().getFullyConverted() = e and
v = i.getDeclaration()
|
if
v instanceof StaticInitializedStaticLocalVariable or
v instanceof GlobalOrNamespaceVariable or
v instanceof Field
if v instanceof StaticInitializedStaticLocalVariable or v instanceof GlobalOrNamespaceVariable
then result = v
else result = e.getEnclosingDeclaration()
)
@@ -179,10 +173,7 @@ Variable getEnclosingVariable0(Expr e) {
i.getExpr().getFullyConverted() = e and
v = i.getDeclaration()
|
if
v instanceof StaticInitializedStaticLocalVariable or
v instanceof GlobalOrNamespaceVariable or
v instanceof Field
if v instanceof StaticInitializedStaticLocalVariable or v instanceof GlobalOrNamespaceVariable
then result = v
else result = e.getEnclosingVariable()
)
@@ -835,46 +826,6 @@ class TranslatedPostfixCrementOperation extends TranslatedCrementOperation {
override Instruction getResult() { result = this.getLoadedOperand().getResult() }
}
class TranslatedParamAccessForType extends TranslatedNonConstantExpr {
override ParamAccessForType expr;
TranslatedParamAccessForType() {
// Currently only needed for this parameter accesses.
expr.isThisAccess()
}
final override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getInstruction(OnlyInstructionTag()) and
kind instanceof GotoEdge
}
override Instruction getALastInstructionInternal() {
result = this.getInstruction(OnlyInstructionTag())
}
final override TranslatedElement getChildInternal(int id) { none() }
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = this.getParent().getChildSuccessor(this, kind)
}
override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::CopyValue and
resultType = getTypeForPRValue(expr.getType())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result =
this.getEnclosingFunction().(TranslatedNonStaticDataMemberVarInit).getLoadThisInstruction()
}
}
/**
* IR translation of an array access expression (e.g. `a[i]`). The array being accessed will either
* be a prvalue of pointer type (possibly due to an implicit array-to-pointer conversion), or a
@@ -1264,7 +1215,7 @@ class TranslatedFunctionAccess extends TranslatedNonConstantExpr {
resultType = this.getResultType()
}
override Declaration getInstructionFunction(InstructionTag tag) {
override Function getInstructionFunction(InstructionTag tag) {
tag = OnlyInstructionTag() and
result = expr.getTarget()
}
@@ -2547,7 +2498,7 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
any()
}
override Declaration getInstructionFunction(InstructionTag tag) {
override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = expr.getAllocator()
}
@@ -2630,7 +2581,7 @@ class TranslatedDeleteOrDeleteArrayExpr extends TranslatedNonConstantExpr, Trans
result = this.getFirstArgumentOrCallInstruction(kind)
}
override Declaration getInstructionFunction(InstructionTag tag) {
override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = expr.getDeallocator()
}

View File

@@ -148,8 +148,7 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn
final override Declaration getFunction() {
result = getEnclosingFunction(expr) or
result = getEnclosingVariable(expr).(GlobalOrNamespaceVariable) or
result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable) or
result = getEnclosingVariable(expr).(Field)
result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable)
}
final override Locatable getAst() { result = expr }
@@ -515,8 +514,8 @@ TranslatedFieldInitialization getTranslatedConstructorFieldInitialization(Constr
}
/**
* The IR translation of the initialization of a field from an element of
* an initializer list.
* Represents the IR translation of the initialization of a field from an
* element of an initializer list.
*/
abstract class TranslatedFieldInitialization extends TranslatedElement {
Expr ast;
@@ -529,11 +528,13 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
final override Declaration getFunction() {
result = getEnclosingFunction(ast) or
result = getEnclosingVariable(ast).(GlobalOrNamespaceVariable) or
result = getEnclosingVariable(ast).(StaticInitializedStaticLocalVariable) or
result = getEnclosingVariable(ast).(Field)
result = getEnclosingVariable(ast).(StaticInitializedStaticLocalVariable)
}
final Field getField() { result = field }
final override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getInstruction(this.getFieldAddressTag()) and
kind instanceof GotoEdge
}
/**
* Gets the zero-based index describing the order in which this field is to be
@@ -541,20 +542,6 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
*/
final int getOrder() { result = field.getInitializationOrder() }
/** Gets the position in the initializer list, or `-1` if the initialization is implicit. */
int getPosition() { result = -1 }
}
/**
* The IR translation of the initialization of a field from an element of an initializer
* list where default initialization is not used.
*/
abstract class TranslatedNonDefaultFieldInitialization extends TranslatedFieldInitialization {
final override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getInstruction(this.getFieldAddressTag()) and
kind instanceof GotoEdge
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = this.getFieldAddressTag() and
opcode instanceof Opcode::FieldAddress and
@@ -572,13 +559,18 @@ abstract class TranslatedNonDefaultFieldInitialization extends TranslatedFieldIn
}
final InstructionTag getFieldAddressTag() { result = InitializerFieldAddressTag() }
final Field getField() { result = field }
/** Gets the position in the initializer list, or `-1` if the initialization is implicit. */
int getPosition() { result = -1 }
}
/**
* The IR translation of the initialization of a field from an explicit element in
* an initializer list.
* Represents the IR translation of the initialization of a field from an
* explicit element in an initializer list.
*/
class TranslatedExplicitFieldInitialization extends TranslatedNonDefaultFieldInitialization,
class TranslatedExplicitFieldInitialization extends TranslatedFieldInitialization,
InitializationContext, TTranslatedExplicitFieldInitialization
{
Expr expr;
@@ -618,81 +610,15 @@ class TranslatedExplicitFieldInitialization extends TranslatedNonDefaultFieldIni
override int getPosition() { result = position }
}
/**
* The IR translation of the initialization of a field from an element of an initializer
* list where default initialization is used.
*/
class TranslatedDefaultFieldInitialization extends TranslatedFieldInitialization,
TTranslatedDefaultFieldInitialization
{
TranslatedDefaultFieldInitialization() {
this = TTranslatedDefaultFieldInitialization(ast, field)
}
final override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getInstruction(CallTargetTag()) and
kind instanceof GotoEdge
}
override Instruction getALastInstructionInternal() {
result = this.getSideEffects().getALastInstruction()
}
override TranslatedElement getLastChild() { result = this.getSideEffects() }
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
tag = CallTargetTag() and
result = this.getInstruction(CallTag())
or
tag = CallTag() and
result = this.getSideEffects().getFirstInstruction(kind)
}
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
child = this.getSideEffects() and
result = this.getParent().getChildSuccessor(this, kind)
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = CallTargetTag() and
opcode instanceof Opcode::FunctionAddress and
resultType = getFunctionGLValueType()
or
tag = CallTag() and
opcode instanceof Opcode::Call and
resultType = getVoidType()
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = CallTag() and
(
operandTag instanceof CallTargetOperandTag and
result = this.getInstruction(CallTargetTag())
or
operandTag instanceof ThisArgumentOperandTag and
result = getTranslatedFunction(this.getFunction()).getLoadThisInstruction()
)
}
override Declaration getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and
result = field
}
override TranslatedElement getChild(int id) { id = 0 and result = this.getSideEffects() }
final TranslatedSideEffects getSideEffects() { result.getExpr() = ast }
}
private string getZeroValue(Type type) {
if type instanceof FloatingPointType then result = "0.0" else result = "0"
}
/**
* The IR translation of the initialization of a field without a corresponding
* element in the initializer list.
* Represents the IR translation of the initialization of a field without a
* corresponding element in the initializer list.
*/
class TranslatedFieldValueInitialization extends TranslatedNonDefaultFieldInitialization,
class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
TTranslatedFieldValueInitialization
{
TranslatedFieldValueInitialization() { this = TTranslatedFieldValueInitialization(ast, field) }
@@ -702,7 +628,7 @@ class TranslatedFieldValueInitialization extends TranslatedNonDefaultFieldInitia
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
TranslatedNonDefaultFieldInitialization.super.hasInstruction(opcode, tag, resultType)
TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType)
or
tag = this.getFieldDefaultValueTag() and
opcode instanceof Opcode::Constant and
@@ -733,8 +659,7 @@ class TranslatedFieldValueInitialization extends TranslatedNonDefaultFieldInitia
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
result =
TranslatedNonDefaultFieldInitialization.super.getInstructionRegisterOperand(tag, operandTag)
result = TranslatedFieldInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
tag = this.getFieldDefaultValueStoreTag() and
(
@@ -758,8 +683,8 @@ class TranslatedFieldValueInitialization extends TranslatedNonDefaultFieldInitia
}
/**
* The IR translation of the initialization of an array element from an element
* of an initializer list.
* Represents the IR translation of the initialization of an array element from
* an element of an initializer list.
*/
abstract class TranslatedElementInitialization extends TranslatedElement {
ArrayOrVectorAggregateLiteral initList;
@@ -776,8 +701,6 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
result = getEnclosingVariable(initList).(GlobalOrNamespaceVariable)
or
result = getEnclosingVariable(initList).(StaticInitializedStaticLocalVariable)
or
result = getEnclosingVariable(initList).(Field)
}
final override Instruction getFirstInstruction(EdgeKind kind) {
@@ -836,8 +759,8 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
}
/**
* The IR translation of the initialization of an array element from an explicit
* element in an initializer list.
* Represents the IR translation of the initialization of an array element from
* an explicit element in an initializer list.
*/
class TranslatedExplicitElementInitialization extends TranslatedElementInitialization,
TTranslatedExplicitElementInitialization, InitializationContext
@@ -885,8 +808,8 @@ class TranslatedExplicitElementInitialization extends TranslatedElementInitializ
}
/**
* The IR translation of the initialization of a range of array elements without
* corresponding elements in the initializer list.
* Represents the IR translation of the initialization of a range of array
* elements without corresponding elements in the initializer list.
*/
class TranslatedElementValueInitialization extends TranslatedElementInitialization,
TTranslatedElementValueInitialization

View File

@@ -1,217 +0,0 @@
import semmle.code.cpp.ir.implementation.raw.internal.TranslatedElement
private import TranslatedExpr
private import cpp
private import semmle.code.cpp.ir.implementation.internal.OperandTag
private import semmle.code.cpp.ir.internal.TempVariableTag
private import semmle.code.cpp.ir.internal.CppType
private import TranslatedInitialization
private import InstructionTag
private import semmle.code.cpp.ir.internal.IRUtilities
class TranslatedNonStaticDataMemberVarInit extends TranslatedRootElement,
TTranslatedNonStaticDataMemberVarInit, InitializationContext
{
Field field;
Class cls;
TranslatedNonStaticDataMemberVarInit() {
this = TTranslatedNonStaticDataMemberVarInit(field) and
cls.getAMember() = field
}
override string toString() { result = cls.toString() + "::" + field.toString() }
final override Field getAst() { result = field }
final override Declaration getFunction() { result = field }
override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getInstruction(EnterFunctionTag()) and
kind instanceof GotoEdge
}
override Instruction getALastInstructionInternal() {
result = this.getInstruction(ExitFunctionTag())
}
override TranslatedElement getChild(int n) {
n = 1 and
result = getTranslatedInitialization(field.getInitializer().getExpr().getFullyConverted())
}
override predicate hasInstruction(Opcode op, InstructionTag tag, CppType type) {
op instanceof Opcode::EnterFunction and
tag = EnterFunctionTag() and
type = getVoidType()
or
op instanceof Opcode::AliasedDefinition and
tag = AliasedDefinitionTag() and
type = getUnknownType()
or
op instanceof Opcode::InitializeNonLocal and
tag = InitializeNonLocalTag() and
type = getUnknownType()
or
tag = ThisAddressTag() and
op instanceof Opcode::VariableAddress and
type = getTypeForGLValue(any(UnknownType t))
or
tag = InitializerStoreTag() and
op instanceof Opcode::InitializeParameter and
type = this.getThisType()
or
tag = ThisLoadTag() and
op instanceof Opcode::Load and
type = this.getThisType()
or
tag = InitializerIndirectStoreTag() and
op instanceof Opcode::InitializeIndirection and
type = getTypeForPRValue(cls)
or
op instanceof Opcode::FieldAddress and
tag = InitializerFieldAddressTag() and
type = getTypeForGLValue(field.getType())
or
op instanceof Opcode::ReturnVoid and
tag = ReturnTag() and
type = getVoidType()
or
op instanceof Opcode::AliasedUse and
tag = AliasedUseTag() and
type = getVoidType()
or
op instanceof Opcode::ExitFunction and
tag = ExitFunctionTag() and
type = getVoidType()
}
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
tag = EnterFunctionTag() and
result = this.getInstruction(AliasedDefinitionTag())
or
tag = AliasedDefinitionTag() and
result = this.getInstruction(InitializeNonLocalTag())
or
tag = InitializeNonLocalTag() and
result = this.getInstruction(ThisAddressTag())
or
tag = ThisAddressTag() and
result = this.getInstruction(InitializerStoreTag())
or
tag = InitializerStoreTag() and
result = this.getInstruction(ThisLoadTag())
or
tag = ThisLoadTag() and
result = this.getInstruction(InitializerIndirectStoreTag())
or
tag = InitializerIndirectStoreTag() and
result = this.getInstruction(InitializerFieldAddressTag())
)
or
tag = InitializerFieldAddressTag() and
result = this.getChild(1).getFirstInstruction(kind)
or
kind instanceof GotoEdge and
(
tag = ReturnTag() and
result = this.getInstruction(AliasedUseTag())
or
tag = AliasedUseTag() and
result = this.getInstruction(ExitFunctionTag())
)
}
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
child = this.getChild(1) and
result = this.getInstruction(ReturnTag()) and
kind instanceof GotoEdge
}
final override CppType getInstructionMemoryOperandType(
InstructionTag tag, TypedOperandTag operandTag
) {
tag = AliasedUseTag() and
operandTag instanceof SideEffectOperandTag and
result = getUnknownType()
}
override IRVariable getInstructionVariable(InstructionTag tag) {
(
tag = ThisAddressTag() or
tag = InitializerStoreTag() or
tag = InitializerIndirectStoreTag()
) and
result = getIRTempVariable(field, ThisTempVar())
}
override Field getInstructionField(InstructionTag tag) {
tag = InitializerFieldAddressTag() and
result = field
}
override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = ThisTempVar() and
type = this.getThisType()
}
/**
* Holds if this variable defines or accesses variable `var` with type `type`. This includes all
* parameters and local variables, plus any global variables or static data members that are
* directly accessed by the function.
*/
final predicate hasUserVariable(Variable varUsed, CppType type) {
(
(
varUsed instanceof GlobalOrNamespaceVariable
or
varUsed instanceof StaticLocalVariable
or
varUsed instanceof MemberVariable and not varUsed instanceof Field
) and
exists(VariableAccess access |
access.getTarget() = varUsed and
getEnclosingVariable(access) = field
)
or
field = varUsed
or
varUsed.(LocalScopeVariable).getEnclosingElement*() = field
or
varUsed.(Parameter).getCatchBlock().getEnclosingElement*() = field
) and
type = getTypeForPRValue(getVariableType(varUsed))
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
(
tag = InitializerStoreTag()
or
tag = ThisLoadTag()
) and
operandTag instanceof AddressOperandTag and
result = this.getInstruction(ThisAddressTag())
or
(
tag = InitializerIndirectStoreTag() and
operandTag instanceof AddressOperandTag
or
tag = InitializerFieldAddressTag() and
operandTag instanceof UnaryOperandTag
) and
result = this.getInstruction(ThisLoadTag())
}
override Instruction getTargetAddress() {
result = this.getInstruction(InitializerFieldAddressTag())
}
override Type getTargetType() { result = field.getUnspecifiedType() }
final Instruction getLoadThisInstruction() { result = this.getInstruction(ThisLoadTag()) }
private CppType getThisType() { result = getTypeForGLValue(cls) }
}
TranslatedNonStaticDataMemberVarInit getTranslatedFieldInit(Field field) { result.getAst() = field }

View File

@@ -390,7 +390,7 @@ class TranslatedDeclStmt extends TranslatedStmt {
override TranslatedElement getLastChild() { result = this.getChild(this.getChildCount() - 1) }
private int getChildCount() { result = count(int i | exists(this.getDeclarationEntry(i))) }
private int getChildCount() { result = count(this.getDeclarationEntry(_)) }
IRDeclarationEntry getIRDeclarationEntry(int index) {
result.hasIndex(index) and

View File

@@ -495,7 +495,7 @@ class FieldInstruction extends Instruction {
* `FunctionAddress` instruction.
*/
class FunctionInstruction extends Instruction {
Language::Declaration funcSymbol;
Language::Function funcSymbol;
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
@@ -504,7 +504,7 @@ class FunctionInstruction extends Instruction {
/**
* Gets the function that this instruction references.
*/
final Language::Declaration getFunctionSymbol() { result = funcSymbol }
final Language::Function getFunctionSymbol() { result = funcSymbol }
}
/**
@@ -1678,7 +1678,7 @@ class CallInstruction extends Instruction {
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
final Language::Declaration getStaticCallTarget() {
final Language::Function getStaticCallTarget() {
result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol()
}

View File

@@ -48,6 +48,7 @@ private import implementations.SqLite3
private import implementations.PostgreSql
private import implementations.System
private import implementations.StructuredExceptionHandling
private import implementations.ZMQ
private import implementations.Win32CommandExecution
private import implementations.CA2AEX
private import implementations.CComBSTR
@@ -57,4 +58,3 @@ private import implementations.CAtlFileMapping
private import implementations.CAtlTemporaryFile
private import implementations.CRegKey
private import implementations.WinHttp
private import implementations.Http

View File

@@ -112,3 +112,21 @@ private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunctio
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
}
/**
* A model for `getc` and similar functions that are flow sources.
*/
private class GetcSource extends SourceModelCsv {
override predicate row(string row) {
row =
[
";;false;getc;;;ReturnValue;remote", ";;false;getwc;;;ReturnValue;remote",
";;false;_getc_nolock;;;ReturnValue;remote", ";;false;_getwc_nolock;;;ReturnValue;remote",
";;false;getch;;;ReturnValue;local", ";;false;_getch;;;ReturnValue;local",
";;false;_getwch;;;ReturnValue;local", ";;false;_getch_nolock;;;ReturnValue;local",
";;false;_getwch_nolock;;;ReturnValue;local", ";;false;getchar;;;ReturnValue;local",
";;false;getwchar;;;ReturnValue;local", ";;false;_getchar_nolock;;;ReturnValue;local",
";;false;_getwchar_nolock;;;ReturnValue;local",
]
}
}

View File

@@ -1,193 +0,0 @@
private import cpp
private import semmle.code.cpp.ir.dataflow.FlowSteps
private import semmle.code.cpp.dataflow.new.DataFlow
private class HttpRequest extends Class {
HttpRequest() { this.hasGlobalName("_HTTP_REQUEST_V1") }
}
private class HttpRequestInheritingContent extends TaintInheritingContent, DataFlow::FieldContent {
HttpRequestInheritingContent() {
this.getAField().getDeclaringType() instanceof HttpRequest and
(
this.getAField().hasName("pRawUrl") and
this.getIndirectionIndex() = 2
or
this.getAField().hasName("CookedUrl") and
this.getIndirectionIndex() = 1
or
this.getAField().hasName("Headers") and
this.getIndirectionIndex() = 1
or
this.getAField().hasName("pEntityChunks") and
this.getIndirectionIndex() = 2
or
this.getAField().hasName("pSslInfo") and
this.getIndirectionIndex() = 2
)
}
}
private class HttpCookedUrl extends Class {
HttpCookedUrl() { this.hasGlobalName("_HTTP_COOKED_URL") }
}
private class HttpCookedUrlInheritingContent extends TaintInheritingContent, DataFlow::FieldContent {
HttpCookedUrlInheritingContent() {
this.getAField().getDeclaringType() instanceof HttpCookedUrl and
this.getAField().hasName(["pFullUrl", "pHost", "pAbsPath", "pQueryString"]) and
this.getIndirectionIndex() = 2
}
}
private class HttpRequestHeaders extends Class {
HttpRequestHeaders() { this.hasGlobalName("_HTTP_REQUEST_HEADERS") }
}
private class HttpRequestHeadersInheritingContent extends TaintInheritingContent,
DataFlow::FieldContent
{
HttpRequestHeadersInheritingContent() {
this.getAField().getDeclaringType() instanceof HttpRequestHeaders and
(
this.getAField().hasName("KnownHeaders") and
this.getIndirectionIndex() = 1
or
this.getAField().hasName("pUnknownHeaders") and
this.getIndirectionIndex() = 2
)
}
}
private class HttpKnownHeader extends Class {
HttpKnownHeader() { this.hasGlobalName("_HTTP_KNOWN_HEADER") }
}
private class HttpKnownHeaderInheritingContent extends TaintInheritingContent,
DataFlow::FieldContent
{
HttpKnownHeaderInheritingContent() {
this.getAField().getDeclaringType() instanceof HttpKnownHeader and
this.getAField().hasName("pRawValue") and
this.getIndirectionIndex() = 2
}
}
private class HttpUnknownHeader extends Class {
HttpUnknownHeader() { this.hasGlobalName("_HTTP_UNKNOWN_HEADER") }
}
private class HttpUnknownHeaderInheritingContent extends TaintInheritingContent,
DataFlow::FieldContent
{
HttpUnknownHeaderInheritingContent() {
this.getAField().getDeclaringType() instanceof HttpUnknownHeader and
this.getAField().hasName(["pName", "pRawValue"]) and
this.getIndirectionIndex() = 2
}
}
private class HttpDataChunk extends Class {
HttpDataChunk() { this.hasGlobalName("_HTTP_DATA_CHUNK") }
}
private class HttpDataChunkInheritingContent extends TaintInheritingContent, DataFlow::FieldContent {
HttpDataChunkInheritingContent() {
this.getAField().getDeclaringType().(Union).getDeclaringType() instanceof HttpDataChunk and
(
this.getAField().hasName("FromMemory") and
this.getIndirectionIndex() = 1
or
this.getAField().hasName("FromFileHandle") and
this.getIndirectionIndex() = 1
or
this.getAField().hasName("FromFragmentCache") and
this.getIndirectionIndex() = 1
or
this.getAField().hasName("FromFragmentCacheEx") and
this.getIndirectionIndex() = 1
or
this.getAField().hasName("Trailers") and
this.getIndirectionIndex() = 1
)
}
}
private class FromMemory extends Class {
FromMemory() {
this.getDeclaringType().(Union).getDeclaringType() instanceof HttpDataChunk and
this.getAField().hasName("pBuffer")
}
}
private class FromMemoryInheritingContent extends TaintInheritingContent, DataFlow::FieldContent {
FromMemoryInheritingContent() {
this.getAField().getDeclaringType() instanceof FromMemory and
this.getAField().hasName("pBuffer") and
this.getIndirectionIndex() = 2
}
}
private class FromFileHandle extends Class {
FromFileHandle() {
this.getDeclaringType().(Union).getDeclaringType() instanceof HttpDataChunk and
this.getAField().hasName("FileHandle")
}
}
private class FromFileHandleInheritingContent extends TaintInheritingContent, DataFlow::FieldContent
{
FromFileHandleInheritingContent() {
this.getAField().getDeclaringType() instanceof FromFileHandle and
this.getIndirectionIndex() = 1 and
this.getAField().hasName("FileHandle")
}
}
private class FromFragmentCacheOrCacheEx extends Class {
FromFragmentCacheOrCacheEx() {
this.getDeclaringType().(Union).getDeclaringType() instanceof HttpDataChunk and
this.getAField().hasName("pFragmentName")
}
}
private class FromFragmentCacheInheritingContent extends TaintInheritingContent,
DataFlow::FieldContent
{
FromFragmentCacheInheritingContent() {
this.getAField().getDeclaringType() instanceof FromFragmentCacheOrCacheEx and
this.getIndirectionIndex() = 2 and
this.getAField().hasName("pFragmentName")
}
}
private class HttpSslInfo extends Class {
HttpSslInfo() { this.hasGlobalName("_HTTP_SSL_INFO") }
}
private class HttpSslInfoInheritingContent extends TaintInheritingContent, DataFlow::FieldContent {
HttpSslInfoInheritingContent() {
this.getAField().getDeclaringType() instanceof HttpSslInfo and
this.getAField().hasName(["pServerCertIssuer", "pServerCertSubject", "pClientCertInfo"]) and
this.getIndirectionIndex() = 2
}
}
private class HttpSslClientCertInfo extends Class {
HttpSslClientCertInfo() { this.hasGlobalName("_HTTP_SSL_CLIENT_CERT_INFO") }
}
private class HttpSslClientCertInfoInheritingContent extends TaintInheritingContent,
DataFlow::FieldContent
{
HttpSslClientCertInfoInheritingContent() {
this.getAField().getDeclaringType() instanceof HttpSslClientCertInfo and
(
this.getAField().hasName("pCertEncoded") and
this.getIndirectionIndex() = 2
or
this.getAField().hasName("Token") and
this.getIndirectionIndex() = 1
)
}
}

View File

@@ -0,0 +1,45 @@
/**
* Provides implementation classes modeling the ZeroMQ networking library.
*/
import semmle.code.cpp.models.interfaces.FlowSource
/**
* Remote flow sources.
*/
private class ZmqSource extends SourceModelCsv {
override predicate row(string row) {
row =
[
";;false;zmq_recv;;;Argument[*1];remote", ";;false;zmq_recvmsg;;;Argument[*1];remote",
";;false;zmq_msg_recv;;;Argument[*0];remote",
]
}
}
/**
* Remote flow sinks.
*/
private class ZmqSinks extends SinkModelCsv {
override predicate row(string row) {
row =
[
";;false;zmq_send;;;Argument[*1];remote-sink",
";;false;zmq_sendmsg;;;Argument[*1];remote-sink",
";;false;zmq_msg_send;;;Argument[*0];remote-sink",
]
}
}
/**
* Flow steps.
*/
private class ZmqSummaries extends SummaryModelCsv {
override predicate row(string row) {
row =
[
";;false;zmq_msg_init_data;;;Argument[*1];Argument[*0];taint",
";;false;zmq_msg_data;;;Argument[*0];ReturnValue[*];taint",
]
}
}

View File

@@ -404,7 +404,7 @@ predicate cmpWithLinearBound(
* For example, if `t` is a signed 32-bit type then holds if `lb` is
* `-2^31` and `ub` is `2^31 - 1`.
*/
private predicate typeBounds0(ArithmeticType t, float lb, float ub) {
private predicate typeBounds(ArithmeticType t, float lb, float ub) {
exists(IntegralType integralType, float limit |
integralType = t and limit = 2.pow(8 * integralType.getSize())
|
@@ -423,42 +423,6 @@ private predicate typeBounds0(ArithmeticType t, float lb, float ub) {
t instanceof FloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
}
/**
* Gets the underlying type for an enumeration `e`.
*
* If the enumeration does not have an explicit type we approximate it using
* the following rules:
* - The result type is always `signed`, and
* - if the largest value fits in an `int` the result is `int`. Otherwise, the
* result is `long`.
*/
private IntegralType getUnderlyingTypeForEnum(Enum e) {
result = e.getExplicitUnderlyingType()
or
not e.hasExplicitUnderlyingType() and
result.isSigned() and
exists(IntType intType |
if max(e.getAnEnumConstant().getValue().toFloat()) >= 2.pow(8 * intType.getSize() - 1)
then result instanceof LongType
else result = intType
)
}
/**
* Holds if `lb` and `ub` are the lower and upper bounds of the unspecified
* type `t`.
*
* For example, if `t` is a signed 32-bit type then holds if `lb` is
* `-2^31` and `ub` is `2^31 - 1`.
*
* Unlike `typeBounds0`, this predicate also handles `Enum` types.
*/
private predicate typeBounds(Type t, float lb, float ub) {
typeBounds0(t, lb, ub)
or
typeBounds0(getUnderlyingTypeForEnum(t), lb, ub)
}
private Type stripReference(Type t) {
if t instanceof ReferenceType then result = t.(ReferenceType).getBaseType() else result = t
}

View File

@@ -512,8 +512,8 @@ private module BoundsEstimate {
*/
float getBoundsLimit() {
// This limit is arbitrary, but low enough that it prevents timeouts on
// specific observed customer databases (and in the tests).
result = 2.0.pow(29)
// specific observed customer databases (and the in the tests).
result = 2.0.pow(40)
}
/** Gets the maximum number of bounds possible for `t` when widening is used. */
@@ -552,47 +552,34 @@ private module BoundsEstimate {
private float nrOfBoundsPhiGuard(RangeSsaDefinition def, StackVariable v) {
// If we have
//
// if (x < c) { e1 } else { e2 }
// e3
//
// then `{ e1 }` and `{ e2 }` are both guard phi nodes guarded by `x < c`.
// The range analysis propagates bounds on `x` into both branches, filtered
// by the condition. In this case all lower bounds flow to `{ e1 }` and only
// lower bounds that are smaller than `c` flow to `{ e2 }`.
//
// The largest number of bounds possible for `e3` is the number of bounds on `x` plus
// one. This happens when all bounds flow from `x` to `e1` to `e3` and the
// bound `c` can flow to `e2` to `e3`.
//
// We want to optimize our bounds estimate for `e3`, as that is the estimate
// that can continue propagating forward. We don't know how the existing
// bounds will be split between the different branches. That depends on
// whether the range analysis is tracking lower bounds or upper bounds, and
// on the meaning of the condition.
//
// As a heuristic we divide the number of bounds on `x` by 2 to "average"
// the effect of the condition and add 1 to account for the bound from the
// condition itself. This will approximate estimates inside the branches,
// but will give a good estimate after the branches are merged.
//
// This also handles cases such as this one
//
// if (x < c) { e1 }
// e3
// e2
//
// where `e3` is both a guard phi node (guarded by `x < c`) and a normal
// phi node (control is merged after the `if` statement). Here half of the
// bounds flow into the branch and then to `e3` as a normal phi node and the
// "other" half flow from the condition to `e3` as a guard phi node.
exists(float varBounds |
// If there's different `access`es, then they refer to the same
// variable with the same lower bounds. Hence adding these guards makes no
// sense (the implementation will take the union, but they'll be removed by
// deduplication). Hence we use `max` as an approximation.
varBounds =
max(VariableAccess access | isGuardPhiWithBound(def, v, access) | nrOfBoundsExpr(access)) and
result = (varBounds + 1) / 2
)
// then `e2` is both a guard phi node (guarded by `x < c`) and a normal
// phi node (control is merged after the `if` statement).
//
// Assume `x` has `n` bounds. Then `n` bounds are propagated to the guard
// phi node `{ e1 }` and, since `{ e1 }` is input to `e2` as a normal phi
// node, `n` bounds are propagated to `e2`. If we also propagate the `n`
// bounds to `e2` as a guard phi node, then we square the number of
// bounds.
//
// However in practice `x < c` is going to cut down the number of bounds:
// The tracked bounds can't flow to both branches as that would require
// them to simultaneously be greater and smaller than `c`. To approximate
// this better, the contribution from a guard phi node that is also a
// normal phi node is 1.
exists(def.getAPhiInput(v)) and
isGuardPhiWithBound(def, v, _) and
result = 1
or
not exists(def.getAPhiInput(v)) and
// If there's different `access`es, then they refer to the same variable
// with the same lower bounds. Hence adding these guards make no sense (the
// implementation will take the union, but they'll be removed by
// deduplication). Hence we use `max` as an approximation.
result =
max(VariableAccess access | isGuardPhiWithBound(def, v, access) | nrOfBoundsExpr(access))
or
def.isPhiNode(v) and
not isGuardPhiWithBound(def, v, _) and
@@ -2193,16 +2180,6 @@ module SimpleRangeAnalysisInternal {
/** Gets the estimate of the number of bounds for `e`. */
float estimateNrOfBounds(Expr e) { result = BoundsEstimate::nrOfBoundsExpr(e) }
/** Counts the numbers of lower bounds that are computed internally for `e`. */
float countNrOfLowerBounds(Expr e) {
result = strictcount(float lb | lb = getLowerBoundsImpl(e) | lb)
}
/** Counts the numbers of upper bounds that are computed internally for `e`. */
float countNrOfUpperBounds(Expr e) {
result = strictcount(float ub | ub = getUpperBoundsImpl(e) | ub)
}
}
/** Provides predicates for debugging the simple range analysis library. */
@@ -2231,7 +2208,7 @@ private module Debug {
*/
predicate countGetLowerBoundsImpl(Expr e, int n) {
e = getRelevantLocatable() and
n = SimpleRangeAnalysisInternal::countNrOfLowerBounds(e)
n = strictcount(float lb | lb = getLowerBoundsImpl(e) | lb)
}
float debugNrOfBounds(Expr e) {

View File

@@ -236,62 +236,6 @@ extractor_version(
string frontend_version: string ref
)
/**
* Gives the TRAP filename that `trap` is associated with.
* For debugging only.
*/
trap_filename(
int trap: @trap,
string filename: string ref
);
/**
* Gives the tag name for `tag`.
* For debugging only.
*/
tag_name(
int tag: @tag,
string name: string ref
);
@trap_or_tag = @tag | @trap;
/**
* Gives the name for the source file.
*/
source_file_name(
int sf: @source_file,
string name: string ref
);
/**
* In `build-mode: none` overlay mode, indicates that `source_file`
* (`/path/to/foo.c`) uses the TRAP file `trap_file`; i.e. it is the
* TRAP file corresponding to `foo.c`, something it transitively
* includes, or a template instantiation it transitively uses.
*/
source_file_uses_trap(
int source_file: @source_file ref,
int trap_file: @trap ref
);
/**
* In `build-mode: none` overlay mode, indicates that the TRAP file
* `trap_file` uses tag `tag`.
*/
trap_uses_tag(
int trap_file: @trap ref,
int tag: @tag ref
);
/**
* Holds if there is a definition of `element` in TRAP file or tag `t`.
*/
in_trap_or_tag(
int element: @element ref,
int t: @trap_or_tag ref
);
pch_uses(
int pch: @pch ref,
int compilation: @compilation ref,

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More