Compare commits

..

2 Commits

Author SHA1 Message Date
Calum Grant
a6818d2da9 WIP 2024-05-10 10:55:27 +01:00
Calum Grant
4ca11abaa9 Refactoring queries WIP 2024-05-09 16:42:52 +01:00
1897 changed files with 56522 additions and 78663 deletions

View File

@@ -10,19 +10,15 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub
build --repo_env=CC=clang --repo_env=CXX=clang++
# we use transitions that break builds of `...`, so for `test` to work with that we need the following
test --build_tests_only
build:linux --cxxopt=-std=c++20
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
# this requires developer mode, but is required to have pack installer functioning
startup --windows_enable_symlinks
common --enable_runfiles
# with the above, we can avoid building python zips which is the default on windows as that's expensive
build --nobuild_python_zip
common --registry=file:///%workspace%/misc/bazel/registry
common --registry=https://bcr.bazel.build
common --@rules_dotnet//dotnet/settings:strict_deps=false
try-import %workspace%/local.bazelrc

View File

@@ -2,9 +2,3 @@
common --registry=file:///%workspace%/ql/misc/bazel/registry
common --registry=https://bcr.bazel.build
# See bazelbuild/rules_dotnet#413: strict_deps in C# also appliy to 3rd-party deps, and when we pull
# in (for example) the xunit package, there's no code in this at all, it just depends transitively on
# its implementation packages without providing any code itself.
# We either can depend on internal implementation details, or turn of strict deps.
common --@rules_dotnet//dotnet/settings:strict_deps=false

View File

@@ -1 +1 @@
7.1.2
7.1.0

34
.gitattributes vendored
View File

@@ -50,40 +50,26 @@
*.dll -text
*.pdb -text
/java/ql/test/stubs/**/*.java linguist-generated=true
/java/ql/test/experimental/stubs/**/*.java linguist-generated=true
/java/kotlin-extractor/deps/*.jar filter=lfs diff=lfs merge=lfs -text
java/ql/test/stubs/**/*.java linguist-generated=true
java/ql/test/experimental/stubs/**/*.java linguist-generated=true
# Force git not to modify line endings for go or html files under the go/ql directory
/go/ql/**/*.go -text
/go/ql/**/*.html -text
go/ql/**/*.go -text
go/ql/**/*.html -text
# Force git not to modify line endings for go dbschemes
/go/*.dbscheme -text
go/*.dbscheme -text
# Preserve unusual line ending from codeql-go merge
/go/extractor/opencsv/CSVReader.java -text
go/extractor/opencsv/CSVReader.java -text
# For some languages, upgrade script testing references really old dbscheme
# files from legacy upgrades that have CRLF line endings. Since upgrade
# resolution relies on object hashes, we must suppress line ending conversion
# for those testing dbscheme files.
/*/ql/lib/upgrades/initial/*.dbscheme -text
*/ql/lib/upgrades/initial/*.dbscheme -text
# Auto-generated modeling for Python
/python/ql/lib/semmle/python/frameworks/data/internal/subclass-capture/*.yml linguist-generated=true
python/ql/lib/semmle/python/frameworks/data/internal/subclass-capture/*.yml linguist-generated=true
# auto-generated bazel lock file
/ruby/extractor/cargo-bazel-lock.json linguist-generated=true
/ruby/extractor/cargo-bazel-lock.json -merge
# auto-generated files for the C# build
/csharp/paket.lock linguist-generated=true
# needs eol=crlf, as `paket` touches this file and saves it as crlf
/csharp/.paket/Paket.Restore.targets linguist-generated=true eol=crlf
/csharp/paket.main.bzl linguist-generated=true
/csharp/paket.main_extension.bzl linguist-generated=true
# ripunzip tool
/misc/ripunzip/ripunzip-* filter=lfs diff=lfs merge=lfs -text
# swift prebuilt resources
/swift/third_party/resource-dir/*.zip filter=lfs diff=lfs merge=lfs -text
ruby/extractor/cargo-bazel-lock.json linguist-generated=true
ruby/extractor/cargo-bazel-lock.json -merge

View File

@@ -1,74 +0,0 @@
name: Build runzip
on:
workflow_dispatch:
inputs:
ripunzip-version:
description: "what reference to checktout from google/runzip"
required: false
default: v1.2.1
openssl-version:
description: "what reference to checkout from openssl/openssl for Linux"
required: false
default: openssl-3.3.0
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-12, windows-2019]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
repository: google/ripunzip
ref: ${{ inputs.ripunzip-version }}
# we need to avoid ripunzip dynamically linking into libssl
# see https://github.com/sfackler/rust-openssl/issues/183
- if: runner.os == 'Linux'
name: checkout openssl
uses: actions/checkout@v4
with:
repository: openssl/openssl
path: openssl
ref: ${{ inputs.openssl-version }}
- if: runner.os == 'Linux'
name: build and install openssl with fPIC
shell: bash
working-directory: openssl
run: |
./config -fPIC --prefix=$HOME/.local --openssldir=$HOME/.local/ssl
make -j $(nproc)
make install_sw -j $(nproc)
- if: runner.os == 'Linux'
name: build (linux)
shell: bash
run: |
env OPENSSL_LIB_DIR=$HOME/.local/lib64 OPENSSL_INCLUDE_DIR=$HOME/.local/include OPENSSL_STATIC=yes cargo build --release
mv target/release/ripunzip ripunzip-linux
- if: runner.os == 'Windows'
name: build (windows)
shell: bash
run: |
cargo build --release
mv target/release/ripunzip ripunzip-windows
- name: build (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
rustup target install x86_64-apple-darwin
rustup target install aarch64-apple-darwin
cargo build --target x86_64-apple-darwin --release
cargo build --target aarch64-apple-darwin --release
lipo -create -output ripunzip-macos \
-arch x86_64 target/x86_64-apple-darwin/release/ripunzip \
-arch arm64 target/aarch64-apple-darwin/release/ripunzip
- uses: actions/upload-artifact@v4
with:
name: ripunzip-${{ runner.os }}
path: ripunzip-*
- name: Check built binary
shell: bash
run: |
./ripunzip-* --version

View File

@@ -56,9 +56,7 @@ jobs:
# uses a compiled language
- run: |
cd csharp
dotnet tool restore
dotnet build .
dotnet build csharp
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@main

View File

@@ -65,7 +65,7 @@ jobs:
key: csharp-qltest-${{ matrix.slice }}
- name: Run QL tests
run: |
codeql test run --threads=0 --ram 50000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
codeql test run --threads=0 --ram 50000 --slice ${{ matrix.slice }} --search-path extractor-pack --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
GITHUB_TOKEN: ${{ github.token }}
unit-tests:
@@ -81,11 +81,10 @@ jobs:
dotnet-version: 8.0.101
- name: Extractor unit tests
run: |
dotnet tool restore
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Util.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Extraction.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.CSharp.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.Cpp.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.1 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
shell: bash
stubgentest:
runs-on: ubuntu-latest
@@ -101,6 +100,6 @@ jobs:
# Update existing stubs in the repo with the freshly generated ones
mv "$STUBS_PATH/output/stubs/_frameworks" ql/test/resources/stubs/
git status
codeql test run --threads=0 --search-path "${{ github.workspace }}" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries -- ql/test/library-tests/dataflow/flowsources/aspremote
codeql test run --threads=0 --search-path extractor-pack --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries -- ql/test/library-tests/dataflow/flowsources/aspremote
env:
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -7,9 +7,8 @@ on:
- .github/workflows/go-tests-other-os.yml
- .github/actions/**
- codeql-workspace.yml
- MODULE.bazel
- .bazelrc
- misc/bazel/**
env:
GO_VERSION: '~1.22.0'
permissions:
contents: read
@@ -19,17 +18,72 @@ jobs:
name: Test MacOS
runs-on: macos-latest
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
id: go
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: go-qltest
- name: Test
run: |
cd go
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
test-win:
if: github.repository_owner == 'github'
name: Test Windows
runs-on: windows-latest-xl
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
id: go
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: go-qltest
- name: Test
run: |
cd go
make test cache="${{ steps.query-cache.outputs.cache-dir }}"

View File

@@ -15,9 +15,9 @@ on:
- .github/workflows/go-tests.yml
- .github/actions/**
- codeql-workspace.yml
- MODULE.bazel
- .bazelrc
- misc/bazel/**
env:
GO_VERSION: '~1.22.0'
permissions:
contents: read
@@ -28,9 +28,51 @@ jobs:
name: Test Linux (Ubuntu)
runs-on: ubuntu-latest-xl
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
id: go
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Check that all Go code is autoformatted
run: |
cd go
make check-formatting
- name: Compile qhelp files to markdown
run: |
cd go
env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
- name: Upload qhelp markdown
uses: actions/upload-artifact@v3
with:
run-code-checks: true
name: qhelp-markdown
path: go/qhelp-out/**/*.md
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: go-qltest
- name: Test
run: |
cd go
make test cache="${{ steps.query-cache.outputs.cache-dir }}"

View File

@@ -1,28 +0,0 @@
name: "Kotlin Build"
on:
pull_request:
paths:
- "java/kotlin-extractor/**"
- "misc/bazel/**"
- "misc/codegen/**"
- "*.bazel*"
- .github/workflows/kotlin-build.yml
branches:
- main
- rc/*
- codeql-cli-*
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
bazel query //java/kotlin-extractor/...
# only build the default version as a quick check that we can build from `codeql`
# the full official build will be checked by QLucie
bazel build //java/kotlin-extractor

View File

@@ -49,20 +49,20 @@ jobs:
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
- name: Release build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd ql; ./scripts/create-extractor-pack.sh
run: cd ql; ./scripts/create-extractor-pack.sh
env:
GH_TOKEN: ${{ github.token }}
GH_TOKEN: ${{ github.token }}
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
with:
key: run-ql-for-ql
- name: Make database and analyze
run: |
./ql/target/release/buramu | tee deprecated.blame # Add a blame file for the extractor to parse.
${CODEQL} database create -l=ql ${DB} --search-path "${{ github.workspace }}"
${CODEQL} database create -l=ql --search-path ql/extractor-pack ${DB}
${CODEQL} database analyze -j0 --format=sarif-latest --output=ql-for-ql.sarif ${DB} ql/ql/src/codeql-suites/ql-code-scanning.qls --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
DB: ${{ runner.temp }}/DB
LGTM_INDEX_FILTERS: |

View File

@@ -53,8 +53,8 @@ jobs:
- name: Create database
run: |
"${CODEQL}" database create \
--search-path "${{ github.workspace }}"
--threads 4 \
--search-path "ql/extractor-pack" \
--threads 4 \
--language ql --source-root "${{ github.workspace }}/repo" \
"${{ runner.temp }}/database"
env:

View File

@@ -49,15 +49,15 @@ jobs:
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
with:
key: ql-for-ql-tests
- name: Run QL tests
run: |
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}" --consistency-queries ql/ql/consistency-queries --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" ql/ql/test
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" ql/ql/test
env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
other-os:
other-os:
strategy:
matrix:
os: [macos-latest, windows-latest]
@@ -65,7 +65,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install GNU tar
- name: Install GNU tar
if: runner.os == 'macOS'
run: |
brew install gnu-tar
@@ -100,7 +100,7 @@ jobs:
- name: Run a single QL tests - Unix
if: runner.os != 'Windows'
run: |
"${CODEQL}" test run --check-databases --search-path "${{ github.workspace }}" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
"${CODEQL}" test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
- name: Run a single QL tests - Windows
@@ -108,4 +108,5 @@ jobs:
shell: pwsh
run: |
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
codeql test run --check-databases --search-path "${{ github.workspace }}" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
codeql test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref

View File

@@ -44,7 +44,7 @@ jobs:
- name: Create database
run: |
codeql database create \
--search-path "${{ github.workspace }}" \
--search-path "${{ github.workspace }}/ruby/extractor-pack" \
--threads 4 \
--language ruby --source-root "${{ github.workspace }}/repo" \
"${{ runner.temp }}/database"

View File

@@ -64,10 +64,10 @@ jobs:
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
with:
key: ruby-qltest
- name: Run QL tests
run: |
codeql test run --threads=0 --ram 50000 --search-path "${{ github.workspace }}" --check-databases --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 }}"
codeql test run --threads=0 --ram 50000 --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --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

@@ -68,6 +68,21 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./swift/actions/run-ql-tests
integration-tests-linux:
if: github.repository_owner == 'github'
needs: build-and-test-linux
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v4
- uses: ./swift/actions/run-integration-tests
integration-tests-macos:
if: ${{ github.repository_owner == 'github' && github.event_name == 'pull_request' }}
needs: build-and-test-macos
runs-on: macos-12-xl
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: ./swift/actions/run-integration-tests
clang-format:
if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest

View File

@@ -1,23 +0,0 @@
name: "Test zipmerge code"
on:
pull_request:
paths:
- "misc/bazel/internal/zipmerge/**"
- "MODULE.bazel"
- ".bazelrc*"
branches:
- main
- "rc/*"
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
bazel test //misc/bazel/internal/zipmerge:test --test_output=all

3
.gitignore vendored
View File

@@ -62,6 +62,3 @@ node_modules/
# Temporary folders for working with generated models
.model-temp
# bazel-built in-tree extractor packs
/*/extractor-pack

View File

@@ -2,6 +2,4 @@
# codeql is publicly forked by many users, and we don't want any LFS file polluting their working
# copies. We therefore exclude everything by default.
# For files required by bazel builds, use rules in `misc/bazel/lfs.bzl` to download them on demand.
# we go for `fetchinclude` to something not exsiting rather than `fetchexclude = *` because the
# former is easier to override (with `git -c` or a local git config) to fetch something specific
fetchinclude = /nothing

View File

@@ -29,13 +29,12 @@ repos:
entry: bazel run //misc/bazel:buildifier
pass_filenames: false
# DISABLED: can be enabled by copying this config and installing `pre-commit` with `--config` on the copy
# - id: go-gen
# name: Check checked in generated files in go
# files: ^go/.*
# language: system
# entry: bazel run //go:gen
# pass_filenames: false
- id: go-gen
name: Check checked in generated files in go
files: ^go/.*
language: system
entry: bazel run //go:gen
pass_filenames: false
- id: codeql-format
name: Fix QL file formatting

View File

@@ -1 +0,0 @@
exports_files(["LICENSE"])

View File

@@ -1,7 +1,6 @@
/cpp/ @github/codeql-c-analysis
/cpp/autobuilder/ @github/codeql-c-extractor
/csharp/ @github/codeql-csharp
/csharp/autobuilder/Semmle.Autobuild.Cpp @github/codeql-c-extractor
/csharp/autobuilder/Semmle.Autobuild.Cpp.Tests @github/codeql-c-extractor
/go/ @github/codeql-go
/java/ @github/codeql-java
/javascript/ @github/codeql-javascript

View File

@@ -22,22 +22,10 @@ bazel_dep(name = "bazel_skylib", version = "1.5.0")
bazel_dep(name = "abseil-cpp", version = "20240116.0", repo_name = "absl")
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
bazel_dep(name = "fmt", version = "10.0.0")
bazel_dep(name = "rules_kotlin", version = "1.9.4-codeql.1")
bazel_dep(name = "gazelle", version = "0.36.0")
bazel_dep(name = "rules_dotnet", version = "0.15.1")
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
dotnet = use_extension("@rules_dotnet//dotnet:extensions.bzl", "dotnet")
dotnet.toolchain(dotnet_version = "8.0.101")
use_repo(dotnet, "dotnet_toolchains")
register_toolchains("@dotnet_toolchains//:all")
csharp_main_extension = use_extension("//csharp:paket.main_extension.bzl", "main_extension")
use_repo(csharp_main_extension, "paket.main")
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse(
hub_name = "codegen_deps",
@@ -66,84 +54,9 @@ node.toolchain(
)
use_repo(node, "nodejs", "nodejs_toolchains")
kotlin_extractor_deps = use_extension("//java/kotlin-extractor:deps.bzl", "kotlin_extractor_deps")
# following list can be kept in sync by running `bazel mod tidy` in `codeql`
use_repo(
kotlin_extractor_deps,
"codeql_kotlin_defaults",
"codeql_kotlin_embeddable",
"kotlin-compiler-1.5.0",
"kotlin-compiler-1.5.10",
"kotlin-compiler-1.5.20",
"kotlin-compiler-1.5.30",
"kotlin-compiler-1.6.0",
"kotlin-compiler-1.6.20",
"kotlin-compiler-1.7.0",
"kotlin-compiler-1.7.20",
"kotlin-compiler-1.8.0",
"kotlin-compiler-1.9.0-Beta",
"kotlin-compiler-1.9.20-Beta",
"kotlin-compiler-2.0.0-RC1",
"kotlin-compiler-embeddable-1.5.0",
"kotlin-compiler-embeddable-1.5.10",
"kotlin-compiler-embeddable-1.5.20",
"kotlin-compiler-embeddable-1.5.30",
"kotlin-compiler-embeddable-1.6.0",
"kotlin-compiler-embeddable-1.6.20",
"kotlin-compiler-embeddable-1.7.0",
"kotlin-compiler-embeddable-1.7.20",
"kotlin-compiler-embeddable-1.8.0",
"kotlin-compiler-embeddable-1.9.0-Beta",
"kotlin-compiler-embeddable-1.9.20-Beta",
"kotlin-compiler-embeddable-2.0.0-RC1",
"kotlin-stdlib-1.5.0",
"kotlin-stdlib-1.5.10",
"kotlin-stdlib-1.5.20",
"kotlin-stdlib-1.5.30",
"kotlin-stdlib-1.6.0",
"kotlin-stdlib-1.6.20",
"kotlin-stdlib-1.7.0",
"kotlin-stdlib-1.7.20",
"kotlin-stdlib-1.8.0",
"kotlin-stdlib-1.9.0-Beta",
"kotlin-stdlib-1.9.20-Beta",
"kotlin-stdlib-2.0.0-RC1",
)
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.22.2")
lfs_files = use_repo_rule("//misc/bazel:lfs.bzl", "lfs_files")
lfs_files(
name = "ripunzip-linux",
srcs = ["//misc/ripunzip:ripunzip-linux"],
executable = True,
)
lfs_files(
name = "ripunzip-windows",
srcs = ["//misc/ripunzip:ripunzip-windows.exe"],
executable = True,
)
lfs_files(
name = "ripunzip-macos",
srcs = ["//misc/ripunzip:ripunzip-macos"],
executable = True,
)
lfs_files(
name = "swift-resource-dir-linux",
srcs = ["//swift/third_party/resource-dir:resource-dir-linux.zip"],
)
lfs_files(
name = "swift-resource-dir-macos",
srcs = ["//swift/third_party/resource-dir:resource-dir-macos.zip"],
)
register_toolchains(
"@nodejs_toolchains//:all",
)

View File

@@ -6,16 +6,19 @@ provide:
- "*/ql/consistency-queries/qlpack.yml"
- "*/ql/automodel/src/qlpack.yml"
- "*/ql/automodel/test/qlpack.yml"
- "*/extractor-pack/codeql-extractor.yml"
- "python/extractor/qlpack.yml"
- "shared/**/qlpack.yml"
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
- "go/ql/config/legacy-support/qlpack.yml"
- "go/build/codeql-extractor-go/codeql-extractor.yml"
- "csharp/ql/campaigns/Solorigate/lib/qlpack.yml"
- "csharp/ql/campaigns/Solorigate/src/qlpack.yml"
- "csharp/ql/campaigns/Solorigate/test/qlpack.yml"
- "misc/legacy-support/*/qlpack.yml"
- "misc/suite-helpers/qlpack.yml"
- "ruby/extractor-pack/codeql-extractor.yml"
- "swift/extractor-pack/codeql-extractor.yml"
- "ql/extractor-pack/codeql-extractor.yml"
- ".github/codeql/extensions/**/codeql-pack.yml"
versionPolicies:

View File

@@ -28,7 +28,6 @@
"/*- Yaml dbscheme -*/",
"/*- Blame dbscheme -*/",
"/*- JSON dbscheme -*/",
"/*- Python dbscheme -*/",
"/*- Empty location -*/"
"/*- Python dbscheme -*/"
]
}

View File

@@ -364,9 +364,5 @@
"Python model summaries test extension": [
"python/ql/test/library-tests/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/library-tests/dataflow/model-summaries/NormalDataflowTest.ext.yml"
],
"shared tree-sitter extractor cargo.toml": [
"shared/tree-sitter-extractor/Cargo.toml",
"ruby/extractor/codeql-extractor-fake-crate/Cargo.toml"
]
}

13
cpp/autobuilder/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
obj/
TestResults/
*.manifest
*.pdb
*.suo
*.mdb
*.vsmdi
csharp.log
**/bin/Debug
**/bin/Release
*.tlog
.vs
*.user

View File

@@ -1 +0,0 @@
The Windows autobuilder that used to live in this directory moved to `csharp/autobuilder/Semmle.Autobuild.Cpp`.

View File

@@ -200,9 +200,9 @@ namespace Semmle.Autobuild.Cpp.Tests
internal class TestDiagnosticWriter : IDiagnosticsWriter
{
public IList<Semmle.Util.DiagnosticMessage> Diagnostics { get; } = new List<Semmle.Util.DiagnosticMessage>();
public IList<DiagnosticMessage> Diagnostics { get; } = new List<DiagnosticMessage>();
public void AddEntry(Semmle.Util.DiagnosticMessage message) => this.Diagnostics.Add(message);
public void AddEntry(DiagnosticMessage message) => this.Diagnostics.Add(message);
public void Dispose() { }
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.6.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Semmle.Autobuild.Cpp\Semmle.Autobuild.Cpp.csproj" />
<ProjectReference Include="..\..\..\csharp\autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,32 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Semmle.Autobuild.Cpp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("GitHub")]
[assembly: AssemblyProduct("CodeQL autobuilder for C++")]
[assembly: AssemblyCopyright("Copyright © GitHub 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Semmle.Autobuild.Cpp</AssemblyName>
<RootNamespace>Semmle.Autobuild.Cpp</RootNamespace>
<ApplicationIcon />
<OutputType>Exe</OutputType>
<StartupObject />
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.8.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\csharp\extractor\Semmle.Util\Semmle.Util.csproj" />
<ProjectReference Include="..\..\..\csharp\autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
description: Revert support for repeated initializers, which are allowed in C with designated initializers.
compatibility: full
aggregate_field_init.rel: reorder aggregate_field_init.rel (@aggregateliteral aggregate, @expr initializer, @membervariable field, int position) aggregate initializer field
aggregate_array_init.rel: reorder aggregate_array_init.rel (@aggregateliteral aggregate, @expr initializer, int element_index, int position) aggregate initializer element_index
aggregate_field_init.rel: reorder aggregate_field_init.rel (int aggregate, int initializer, int field, int position) aggregate initializer field
aggregate_array_init.rel: reorder aggregate_array_init.rel (int aggregate, int initializer, int element_index, int position) aggregate initializer element_index

View File

@@ -6,7 +6,7 @@ pkg_files(
["**"],
exclude = ["BUILD.bazel"],
),
prefix = "downgrades",
prefix = "cpp/downgrades",
strip_prefix = strip_prefix.from_pkg(),
visibility = ["//cpp:__pkg__"],
)

View File

@@ -5,9 +5,11 @@ package(default_visibility = ["//cpp:__pkg__"])
pkg_files(
name = "dbscheme",
srcs = ["semmlecode.cpp.dbscheme"],
prefix = "cpp",
)
pkg_files(
name = "dbscheme-stats",
srcs = ["semmlecode.cpp.dbscheme.stats"],
prefix = "cpp",
)

View File

@@ -1,23 +1,3 @@
## 1.1.0
### New Features
* Data models can now be added with data extensions. In this way source, sink and summary models can be added in extension `.model.yml` files, rather than by writing classes in QL code. New models should be added in the `lib/ext` folder.
### Minor Analysis Improvements
* A partial model for the `Boost.Asio` network library has been added. This includes sources, sinks and summaries for certain functions in `Boost.Asio`, such as `read_until` and `write`.
## 1.0.0
### Breaking Changes
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
## 0.13.1
No user-facing changes.
## 0.13.0
### Breaking Changes

View File

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

View File

@@ -1,5 +0,0 @@
## 1.0.0
### Breaking Changes
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.

View File

@@ -1,9 +0,0 @@
## 1.1.0
### New Features
* Data models can now be added with data extensions. In this way source, sink and summary models can be added in extension `.model.yml` files, rather than by writing classes in QL code. New models should be added in the `lib/ext` folder.
### Minor Analysis Improvements
* A partial model for the `Boost.Asio` network library has been added. This includes sources, sinks and summaries for certain functions in `Boost.Asio`, such as `read_until` and `write`.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.1.0
lastReleaseVersion: 0.13.0

View File

@@ -1,26 +0,0 @@
extensions:
# partial model of the Boost::Asio network library
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: sourceModel
data: # namespace, type, subtypes, name, signature, ext, output, kind, provenance
- ["boost::asio", "", False, "read", "", "", "Argument[*1]", "remote", "manual"]
- ["boost::asio", "", False, "read_at", "", "", "Argument[*2]", "remote", "manual"]
- ["boost::asio", "", False, "read_until", "", "", "Argument[*1]", "remote", "manual"]
- ["boost::asio", "", False, "async_read", "", "", "Argument[*1]", "remote", "manual"]
- ["boost::asio", "", False, "async_read_at", "", "", "Argument[*2]", "remote", "manual"]
- ["boost::asio", "", False, "async_read_until", "", "", "Argument[*1]", "remote", "manual"]
- addsTo:
pack: codeql/cpp-all
extensible: sinkModel
data: # namespace, type, subtypes, name, signature, ext, input, kind, provenance
- ["boost::asio", "", False, "write", "", "", "Argument[*1]", "remote-sink", "manual"]
- ["boost::asio", "", False, "write_at", "", "", "Argument[*2]", "remote-sink", "manual"]
- ["boost::asio", "", False, "async_write", "", "", "Argument[*1]", "remote-sink", "manual"]
- ["boost::asio", "", False, "async_write_at", "", "", "Argument[*2]", "remote-sink", "manual"]
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
- ["boost::asio", "", False, "buffer", "", "", "Argument[*0]", "ReturnValue", "taint", "manual"]

View File

@@ -1,15 +0,0 @@
extensions:
# Make sure that the extensible model predicates have at least one definition
# to avoid errors about undefined extensionals.
- addsTo:
pack: codeql/cpp-all
extensible: sourceModel
data: []
- addsTo:
pack: codeql/cpp-all
extensible: sinkModel
data: []
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: []

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 1.1.0
version: 0.13.1-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
@@ -14,6 +14,4 @@ dependencies:
codeql/tutorial: ${workspace}
codeql/util: ${workspace}
codeql/xml: ${workspace}
dataExtensions:
- ext/*.model.yml
warnOnImplicitThis: true

View File

@@ -410,10 +410,6 @@ class LocalVariable extends LocalScopeVariable, @localvariable {
or
orphaned_variables(underlyingElement(this), unresolveElement(result))
}
override predicate isStatic() {
super.isStatic() or orphaned_variables(underlyingElement(this), _)
}
}
/**

View File

@@ -375,33 +375,6 @@ cached
class IRGuardCondition extends Instruction {
Instruction branch;
/*
* An `IRGuardCondition` supports reasoning about four different kinds of
* relations:
* 1. A unary equality relation of the form `e == k`
* 2. A binary equality relation of the form `e1 == e2 + k`
* 3. A unary inequality relation of the form `e < k`
* 4. A binary inequality relation of the form `e1 < e2 + k`
*
* where `k` is a constant.
*
* Furthermore, the unary relations (i.e., case 1 and case 3) are also
* inferred from `switch` statement guards: equality relations are inferred
* from the unique `case` statement, if any, and inequality relations are
* inferred from the [case range](https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html)
* gcc extension.
*
* The implementation of all four follows the same structure: Each relation
* has a cached user-facing predicate that. For example,
* `GuardCondition::comparesEq` calls `compares_eq`. This predicate has
* several cases that recursively decompose the relation to bring it to a
* canonical form (i.e., a relation of the form `e1 == e2 + k`). The base
* case for this relation (i.e., `simple_comparison_eq`) handles
* `CompareEQInstruction`s and `CompareNEInstruction`, and recursive
* predicates (e.g., `complex_eq`) rewrites larger expressions such as
* `e1 + k1 == e2 + k2` into canonical the form `e1 == e2 + (k2 - k1)`.
*/
cached
IRGuardCondition() { branch = getBranchForCondition(this) }
@@ -592,7 +565,7 @@ class IRGuardCondition extends Instruction {
/** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `value`. */
cached
predicate comparesEq(Operand op, int k, boolean areEqual, AbstractValue value) {
unary_compares_eq(this, op, k, areEqual, false, value)
compares_eq(this, op, k, areEqual, value)
}
/**
@@ -613,7 +586,7 @@ class IRGuardCondition extends Instruction {
cached
predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) {
exists(AbstractValue value |
unary_compares_eq(this, op, k, areEqual, false, value) and this.valueControls(block, value)
compares_eq(this, op, k, areEqual, value) and this.valueControls(block, value)
)
}
@@ -638,7 +611,7 @@ class IRGuardCondition extends Instruction {
cached
predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) {
exists(AbstractValue value |
unary_compares_eq(this, op, k, areEqual, false, value) and
compares_eq(this, op, k, areEqual, value) and
this.valueControlsEdge(pred, succ, value)
)
}
@@ -764,68 +737,26 @@ private predicate compares_eq(
)
}
/**
* Holds if `op == k` is `areEqual` given that `test` is equal to `value`.
*
* Many internal predicates in this file have a `inNonZeroCase` column.
* Ideally, the `k` column would be a type such as `Option<int>::Option`, to
* represent whether we have a concrete value `k` such that `op == k`, or whether
* we only know that `op != 0`.
* However, cannot instantiate `Option` with an infinite type. Thus the boolean
* `inNonZeroCase` is used to distinquish the `Some` (where we have a concrete
* value `k`) and `None` cases (where we only know that `op != 0`).
*
* Thus, if `inNonZeroCase = true` then `op != 0` and the value of `k` is
* meaningless.
*
* To see why `inNonZeroCase` is needed consider the following C program:
* ```c
* char* p = ...;
* if(p) {
* use(p);
* }
* ```
* in C++ there would be an int-to-bool conversion on `p`. However, since C
* does not have booleans there is no conversion. We want to be able to
* conclude that `p` is non-zero in the true branch, so we need to give `k`
* some value. However, simply setting `k = 1` would make the rest of the
* analysis think that `k == 1` holds inside the branch. So we distinquish
* between the above case and
* ```c
* if(p == 1) {
* use(p)
* }
* ```
* by setting `inNonZeroCase` to `true` in the former case, but not in the
* latter.
*/
private predicate unary_compares_eq(
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
/** Holds if `op == k` is `areEqual` given that `test` is equal to `value`. */
private predicate compares_eq(
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
) {
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
exists(AbstractValue v |
unary_simple_comparison_eq(test, k, inNonZeroCase, v) and op.getDef() = test
|
exists(AbstractValue v | simple_comparison_eq(test, op, k, v) |
areEqual = true and value = v
or
areEqual = false and value = v.getDualValue()
)
or
unary_complex_eq(test, op, k, areEqual, inNonZeroCase, value)
complex_eq(test, op, k, areEqual, value)
or
/* (x is true => (op == k)) => (!x is false => (op == k)) */
exists(AbstractValue dual, boolean inNonZeroCase0 |
value = dual.getDualValue() and
unary_compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, inNonZeroCase0, areEqual, dual)
|
k = 0 and inNonZeroCase = inNonZeroCase0
or
k != 0 and inNonZeroCase = true
exists(AbstractValue dual | value = dual.getDualValue() |
compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, areEqual, dual)
)
or
// ((test is `areEqual` => op == const + k2) and const == `k1`) =>
// test is `areEqual` => op == k1 + k2
inNonZeroCase = false and
exists(int k1, int k2, ConstantInstruction const |
compares_eq(test, op, const.getAUse(), k2, areEqual, value) and
int_value(const) = k1 and
@@ -850,63 +781,35 @@ private predicate simple_comparison_eq(
value.(BooleanValue).getValue() = false
}
/**
* Rearrange various simple comparisons into `op == k` form.
*/
private predicate unary_simple_comparison_eq(
Instruction test, int k, boolean inNonZeroCase, AbstractValue value
) {
/** Rearrange various simple comparisons into `op == k` form. */
private predicate simple_comparison_eq(Instruction test, Operand op, int k, AbstractValue value) {
exists(SwitchInstruction switch, CaseEdge case |
test = switch.getExpression() and
op.getDef() = test and
case = value.(MatchValue).getCase() and
exists(switch.getSuccessor(case)) and
case.getValue().toInt() = k and
inNonZeroCase = false
case.getValue().toInt() = k
)
or
// Any instruction with an integral type could potentially be part of a
// check for nullness when used in a guard. So we include all integral
// typed instructions here. However, since some of these instructions are
// already included as guards in other cases, we exclude those here.
// These are instructions that compute a binary equality or inequality
// relation. For example, the following:
// ```cpp
// if(a == b + 42) { ... }
// ```
// generates the following IR:
// ```
// r1(glval<int>) = VariableAddress[a] :
// r2(int) = Load[a] : &:r1, m1
// r3(glval<int>) = VariableAddress[b] :
// r4(int) = Load[b] : &:r3, m2
// r5(int) = Constant[42] :
// r6(int) = Add : r4, r5
// r7(bool) = CompareEQ : r2, r6
// v1(void) = ConditionalBranch : r7
// ```
// and since `r7` is an integral typed instruction this predicate could
// include a case for when `r7` evaluates to true (in which case we would
// infer that `r6` was non-zero, and a case for when `r7` evaluates to false
// (in which case we would infer that `r6` was zero).
// However, since `a == b + 42` is already supported when reasoning about
// binary equalities we exclude those cases here.
not test.isGLValue() and
not simple_comparison_eq(test, _, _, _, _) and
not simple_comparison_lt(test, _, _, _) and
not test = any(SwitchInstruction switch).getExpression() and
(
test.getResultIRType() instanceof IRAddressType or
test.getResultIRType() instanceof IRIntegerType or
test.getResultIRType() instanceof IRBooleanType
) and
(
k = 1 and
value.(BooleanValue).getValue() = true and
inNonZeroCase = true
or
// There's no implicit CompareInstruction in files compiled as C since C
// doesn't have implicit boolean conversions. So instead we check whether
// there's a branch on a value of pointer or integer type.
exists(ConditionalBranchInstruction branch, IRType type |
not test instanceof CompareInstruction and
type = test.getResultIRType() and
(type instanceof IRAddressType or type instanceof IRIntegerType) and
test = branch.getCondition() and
op.getDef() = test
|
// We'd like to also include a case such as:
// ```
// k = 1 and
// value.(BooleanValue).getValue() = true
// ```
// but all we know is that the value is non-zero in the true branch.
// So we can only conclude something in the false branch.
k = 0 and
value.(BooleanValue).getValue() = false and
inNonZeroCase = false
value.(BooleanValue).getValue() = false
)
}
@@ -918,12 +821,12 @@ private predicate complex_eq(
add_eq(cmp, left, right, k, areEqual, value)
}
private predicate unary_complex_eq(
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
private predicate complex_eq(
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
) {
unary_sub_eq(test, op, k, areEqual, inNonZeroCase, value)
sub_eq(test, op, k, areEqual, value)
or
unary_add_eq(test, op, k, areEqual, inNonZeroCase, value)
add_eq(test, op, k, areEqual, value)
}
/*
@@ -952,8 +855,7 @@ private predicate compares_lt(
/** Holds if `op < k` evaluates to `isLt` given that `test` evaluates to `value`. */
private predicate compares_lt(Instruction test, Operand op, int k, boolean isLt, AbstractValue value) {
unary_simple_comparison_lt(test, k, isLt, value) and
op.getDef() = test
simple_comparison_lt(test, op, k, isLt, value)
or
complex_lt(test, op, k, isLt, value)
or
@@ -1000,11 +902,12 @@ private predicate simple_comparison_lt(CompareInstruction cmp, Operand left, Ope
}
/** Rearrange various simple comparisons into `op < k` form. */
private predicate unary_simple_comparison_lt(
Instruction test, int k, boolean isLt, AbstractValue value
private predicate simple_comparison_lt(
Instruction test, Operand op, int k, boolean isLt, AbstractValue value
) {
exists(SwitchInstruction switch, CaseEdge case |
test = switch.getExpression() and
op.getDef() = test and
case = value.(MatchValue).getCase() and
exists(switch.getSuccessor(case)) and
case.getMaxValue() > case.getMinValue()
@@ -1187,20 +1090,16 @@ private predicate sub_eq(
}
// op - x == c => op == (c+x)
private predicate unary_sub_eq(
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
) {
inNonZeroCase = false and
private predicate sub_eq(Instruction test, Operand op, int k, boolean areEqual, AbstractValue value) {
exists(SubInstruction sub, int c, int x |
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
compares_eq(test, sub.getAUse(), c, areEqual, value) and
op = sub.getLeftOperand() and
x = int_value(sub.getRight()) and
k = c + x
)
or
inNonZeroCase = false and
exists(PointerSubInstruction sub, int c, int x |
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
compares_eq(test, sub.getAUse(), c, areEqual, value) and
op = sub.getLeftOperand() and
x = int_value(sub.getRight()) and
k = c + x
@@ -1254,13 +1153,11 @@ private predicate add_eq(
}
// left + x == right + c => left == right + (c-x)
private predicate unary_add_eq(
Instruction test, Operand left, int k, boolean areEqual, boolean inNonZeroCase,
AbstractValue value
private predicate add_eq(
Instruction test, Operand left, int k, boolean areEqual, AbstractValue value
) {
inNonZeroCase = false and
exists(AddInstruction lhs, int c, int x |
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
@@ -1269,9 +1166,8 @@ private predicate unary_add_eq(
k = c - x
)
or
inNonZeroCase = false and
exists(PointerAddInstruction lhs, int c, int x |
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or

View File

@@ -78,7 +78,6 @@ private import internal.FlowSummaryImpl
private import internal.FlowSummaryImpl::Public
private import internal.FlowSummaryImpl::Private
private import internal.FlowSummaryImpl::Private::External
private import internal.ExternalFlowExtensions as Extensions
private import codeql.mad.ModelValidation as SharedModelVal
private import codeql.util.Unit
@@ -139,9 +138,6 @@ predicate sourceModel(
row.splitAt(";", 7) = kind
) and
provenance = "manual"
or
Extensions::sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance,
_)
}
/** Holds if a sink model exists for the given parameters. */
@@ -162,8 +158,6 @@ predicate sinkModel(
row.splitAt(";", 7) = kind
) and
provenance = "manual"
or
Extensions::sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance, _)
}
/** Holds if a summary model exists for the given parameters. */
@@ -185,9 +179,6 @@ predicate summaryModel(
row.splitAt(";", 8) = kind
) and
provenance = "manual"
or
Extensions::summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind,
provenance, _)
}
private predicate relevantNamespace(string namespace) {
@@ -212,10 +203,8 @@ private predicate canonicalNamespaceLink(string namespace, string subns) {
}
/**
* Holds if MaD framework coverage of `namespace` is `n` api endpoints of the
* kind `(kind, part)`, and `namespaces` is the number of subnamespaces of
* `namespace` which have MaD framework coverage (including `namespace`
* itself).
* Holds if CSV framework coverage of `namespace` is `n` api endpoints of the
* kind `(kind, part)`.
*/
predicate modelCoverage(string namespace, int namespaces, string kind, string part, int n) {
namespaces = strictcount(string subns | canonicalNamespaceLink(namespace, subns)) and
@@ -332,10 +321,10 @@ module CsvValidation {
or
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
|
not namespace.regexpMatch("[a-zA-Z0-9_\\.:]*") and
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
or
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]*") and
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and
result = "Dubious type \"" + type + "\" in " + pred + " model."
or
not name.regexpMatch("[a-zA-Z0-9_<>,]*") and

View File

@@ -9,7 +9,7 @@ private import DataFlowUtil
/**
* Gets a function that might be called by `call`.
*/
DataFlowCallable viableCallable(DataFlowCall call) {
Function viableCallable(DataFlowCall call) {
result = call.(Call).getTarget()
or
// If the target of the call does not have a body in the snapshot, it might

View File

@@ -242,17 +242,7 @@ class CastNode extends Node {
CastNode() { none() } // stub implementation
}
class DataFlowCallable extends Function {
/** Gets a best-effort total ordering. */
int totalorder() {
this =
rank[result](DataFlowCallable c, string file, int startline, int startcolumn |
c.getLocation().hasLocationInfo(file, startline, startcolumn, _, _)
|
c order by file, startline, startcolumn
)
}
}
class DataFlowCallable = Function;
class DataFlowExpr = Expr;
@@ -271,28 +261,10 @@ class DataFlowCall extends Expr instanceof Call {
ExprNode getNode() { result.getExpr() = this }
/** Gets the enclosing callable of this call. */
DataFlowCallable getEnclosingCallable() { result = this.getEnclosingFunction() }
/** Gets a best-effort total ordering. */
int totalorder() {
this =
rank[result](DataFlowCall c, int startline, int startcolumn |
c.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
c order by startline, startcolumn
)
}
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
}
class NodeRegion instanceof Unit {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { none() }
int totalOrder() { result = 1 }
}
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() } // stub implementation
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
/**
* Holds if access paths with `c` at their head always should be tracked at high

View File

@@ -1,27 +0,0 @@
/**
* This module provides extensible predicates for defining MaD models.
*/
/**
* Holds if an external source model exists for the given parameters.
*/
extensible predicate sourceModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string output, string kind, string provenance, QlBuiltins::ExtensionId madId
);
/**
* Holds if an external sink model exists for the given parameters.
*/
extensible predicate sinkModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string kind, string provenance, QlBuiltins::ExtensionId madId
);
/**
* Holds if an external summary model exists for the given parameters.
*/
extensible predicate summaryModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string output, string kind, string provenance, QlBuiltins::ExtensionId madId
);

View File

@@ -1062,16 +1062,6 @@ class DataFlowCallable extends TDataFlowCallable {
result = this.asSummarizedCallable() or // SummarizedCallable = Function (in CPP)
result = this.asSourceCallable()
}
/** Gets a best-effort total ordering. */
int totalorder() {
this =
rank[result](DataFlowCallable c, string file, int startline, int startcolumn |
c.getLocation().hasLocationInfo(file, startline, startcolumn, _, _)
|
c order by file, startline, startcolumn
)
}
}
/**
@@ -1169,16 +1159,6 @@ class DataFlowCall extends TDataFlowCall {
* Gets the location of this call.
*/
Location getLocation() { none() }
/** Gets a best-effort total ordering. */
int totalorder() {
this =
rank[result](DataFlowCall c, int startline, int startcolumn |
c.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
c order by startline, startcolumn
)
}
}
/**
@@ -1267,53 +1247,43 @@ module IsUnreachableInCall {
any(G::IRGuardCondition guard).ensuresLt(left, right, k, block, areEqual)
}
class NodeRegion instanceof IRBlock {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { this = n.getBasicBlock() }
int totalOrder() {
this =
rank[result](IRBlock b, int startline, int startcolumn |
b.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
b order by startline, startcolumn
)
}
}
predicate isUnreachableInCall(NodeRegion block, DataFlowCall call) {
predicate isUnreachableInCall(Node n, DataFlowCall call) {
exists(
InstructionDirectParameterNode paramNode, ConstantIntegralTypeArgumentNode arg,
IntegerConstantInstruction constant, int k, Operand left, Operand right, int argval
IntegerConstantInstruction constant, int k, Operand left, Operand right, IRBlock block
|
// arg flows into `paramNode`
DataFlowImplCommon::viableParamArg(call, pragma[only_bind_into](paramNode),
pragma[only_bind_into](arg)) and
DataFlowImplCommon::viableParamArg(call, paramNode, arg) and
left = constant.getAUse() and
right = valueNumber(paramNode.getInstruction()).getAUse() and
argval = arg.getValue()
block = n.getBasicBlock()
|
// and there's a guard condition which ensures that the result of `left == right + k` is `areEqual`
exists(boolean areEqual | ensuresEq(left, right, k, block, areEqual) |
exists(boolean areEqual |
ensuresEq(pragma[only_bind_into](left), pragma[only_bind_into](right),
pragma[only_bind_into](k), pragma[only_bind_into](block), areEqual)
|
// this block ensures that left = right + k, but it holds that `left != right + k`
areEqual = true and
constant.getValue().toInt() != argval + k
constant.getValue().toInt() != arg.getValue() + k
or
// this block ensures that or `left != right + k`, but it holds that `left = right + k`
areEqual = false and
constant.getValue().toInt() = argval + k
constant.getValue().toInt() = arg.getValue() + k
)
or
// or there's a guard condition which ensures that the result of `left < right + k` is `isLessThan`
exists(boolean isLessThan | ensuresLt(left, right, k, block, isLessThan) |
exists(boolean isLessThan |
ensuresLt(pragma[only_bind_into](left), pragma[only_bind_into](right),
pragma[only_bind_into](k), pragma[only_bind_into](block), isLessThan)
|
isLessThan = true and
// this block ensures that `left < right + k`, but it holds that `left >= right + k`
constant.getValue().toInt() >= argval + k
constant.getValue().toInt() >= arg.getValue() + k
or
// this block ensures that `left >= right + k`, but it holds that `left < right + k`
isLessThan = false and
constant.getValue().toInt() < argval + k
constant.getValue().toInt() < arg.getValue() + k
)
)
}
@@ -1336,8 +1306,6 @@ predicate nodeIsHidden(Node n) {
n instanceof FinalGlobalValue
or
n instanceof InitialGlobalValue
or
n instanceof SsaPhiInputNode
}
predicate neverSkipInPathGraph(Node n) {
@@ -1636,8 +1604,6 @@ private Instruction getAnInstruction(Node n) {
or
result = n.(SsaPhiNode).getPhiNode().getBasicBlock().getFirstInstruction()
or
result = n.(SsaPhiInputNode).getBasicBlock().getFirstInstruction()
or
n.(IndirectInstruction).hasInstructionAndIndirectionIndex(result, _)
or
not n instanceof IndirectInstruction and
@@ -1767,7 +1733,7 @@ module IteratorFlow {
crementCall = def.getValue().asInstruction().(StoreInstruction).getSourceValue() and
sv = def.getSourceVariable() and
bb.getInstruction(i) = crementCall and
Ssa::ssaDefReachesReadExt(sv, result.asDef(), bb, i)
Ssa::ssaDefReachesRead(sv, result.asDef(), bb, i)
)
}
@@ -1801,7 +1767,7 @@ module IteratorFlow {
isIteratorWrite(writeToDeref, address) and
operandForFullyConvertedCall(address, starCall) and
bbStar.getInstruction(iStar) = starCall and
Ssa::ssaDefReachesReadExt(_, def.asDef(), bbStar, iStar) and
Ssa::ssaDefReachesRead(_, def.asDef(), bbStar, iStar) and
ultimate = getAnUltimateDefinition*(def) and
beginStore = ultimate.getValue().asInstruction() and
operandForFullyConvertedCall(beginStore.getSourceValueOperand(), beginCall)

View File

@@ -45,7 +45,6 @@ private newtype TIRDataFlowNode =
or
Ssa::isModifiableByCall(operand, indirectionIndex)
} or
TSsaPhiInputNode(Ssa::PhiNode phi, IRBlock input) { phi.hasInputFromBlock(_, _, _, _, input) } or
TSsaPhiNode(Ssa::PhiNode phi) or
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
@@ -115,13 +114,6 @@ predicate conversionFlow(
instrTo.(CheckedConvertOrNullInstruction).getUnaryOperand() = opFrom
or
instrTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom
or
exists(BuiltInInstruction builtIn |
builtIn = instrTo and
// __builtin_bit_cast
builtIn.getBuiltInOperation() instanceof BuiltInBitCast and
opFrom = builtIn.getAnOperand()
)
)
or
additional = true and
@@ -166,12 +158,6 @@ class Node extends TIRDataFlowNode {
/** Gets the operands corresponding to this node, if any. */
Operand asOperand() { result = this.(OperandNode).getOperand() }
/**
* Gets the operand that is indirectly tracked by this node behind `index`
* number of indirections.
*/
Operand asIndirectOperand(int index) { hasOperandAndIndex(this, result, index) }
/**
* Holds if this node is at index `i` in basic block `block`.
*
@@ -184,9 +170,6 @@ class Node extends TIRDataFlowNode {
or
this.(SsaPhiNode).getPhiNode().getBasicBlock() = block and i = -1
or
this.(SsaPhiInputNode).getBlock() = block and
i = block.getInstructionCount()
or
this.(RawIndirectOperand).getOperand().getUse() = block.getInstruction(i)
or
this.(RawIndirectInstruction).getInstruction() = block.getInstruction(i)
@@ -639,7 +622,7 @@ class SsaPhiNode extends Node, TSsaPhiNode {
final override Location getLocationImpl() { result = phi.getBasicBlock().getLocation() }
override string toStringImpl() { result = phi.toString() }
override string toStringImpl() { result = "Phi" }
/**
* Gets a node that is used as input to this phi node.
@@ -648,7 +631,7 @@ class SsaPhiNode extends Node, TSsaPhiNode {
*/
cached
final Node getAnInput(boolean fromBackEdge) {
result.(SsaPhiInputNode).getPhiNode() = phi and
localFlowStep(result, this) and
exists(IRBlock bPhi, IRBlock bResult |
bPhi = phi.getBasicBlock() and bResult = result.getBasicBlock()
|
@@ -671,58 +654,6 @@ class SsaPhiNode extends Node, TSsaPhiNode {
predicate isPhiRead() { phi.isPhiRead() }
}
/**
* INTERNAL: Do not use.
*
* A node that is used as an input to a phi node.
*
* This class exists to allow more powerful barrier guards. Consider this
* example:
*
* ```cpp
* int x = source();
* if(!safe(x)) {
* x = clear();
* }
* // phi node for x here
* sink(x);
* ```
*
* At the phi node for `x` it is neither the case that `x` is dominated by
* `safe(x)`, or is the case that the phi is dominated by a clearing of `x`.
*
* By inserting a "phi input" node as the last entry in the basic block that
* defines the inputs to the phi we can conclude that each of those inputs are
* safe to pass to `sink`.
*/
class SsaPhiInputNode extends Node, TSsaPhiInputNode {
Ssa::PhiNode phi;
IRBlock block;
SsaPhiInputNode() { this = TSsaPhiInputNode(phi, block) }
/** Gets the phi node associated with this node. */
Ssa::PhiNode getPhiNode() { result = phi }
/** Gets the basic block in which this input originates. */
IRBlock getBlock() { result = block }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() }
override DataFlowType getType() { result = this.getSourceVariable().getType() }
override predicate isGLValue() { phi.getSourceVariable().isGLValue() }
final override Location getLocationImpl() { result = block.getLastInstruction().getLocation() }
override string toStringImpl() { result = "Phi input" }
/** Gets the source variable underlying this phi node. */
Ssa::SourceVariable getSourceVariable() { result = phi.getSourceVariable() }
}
/**
* INTERNAL: do not use.
*
@@ -2245,9 +2176,6 @@ private module Cached {
// Def-use/Use-use flow
Ssa::ssaFlow(nodeFrom, nodeTo)
or
// Phi input -> Phi
nodeFrom.(SsaPhiInputNode).getPhiNode() = nodeTo.(SsaPhiNode).getPhiNode()
or
IteratorFlow::localFlowStep(nodeFrom, nodeTo)
or
// Operand -> Instruction flow
@@ -2686,22 +2614,6 @@ class ContentSet instanceof Content {
}
}
pragma[nomagic]
private predicate guardControlsPhiInput(
IRGuardCondition g, boolean branch, Ssa::Definition def, IRBlock input, Ssa::PhiNode phi
) {
phi.hasInputFromBlock(def, _, _, _, input) and
(
g.controls(input, branch)
or
exists(EdgeKind kind |
g.getBlock() = input and
kind = getConditionalEdge(branch) and
input.getSuccessor(kind) = phi.getBasicBlock()
)
)
}
/**
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
*
@@ -2750,21 +2662,13 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
*
* NOTE: If an indirect expression is tracked, use `getAnIndirectBarrierNode` instead.
*/
Node getABarrierNode() {
ExprNode getABarrierNode() {
exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge |
e = value.getAnInstruction().getConvertedResultExpression() and
result.asConvertedExpr() = e and
result.getConvertedExpr() = e and
guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and
g.controls(result.getBasicBlock(), edge)
)
or
exists(
IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi
|
guardChecks(g, def.getARead().asOperand().getDef().getConvertedResultExpression(), branch) and
guardControlsPhiInput(g, branch, def, input, phi) and
result = TSsaPhiInputNode(phi, input)
)
}
/**
@@ -2800,7 +2704,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
*
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
*/
Node getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) }
IndirectExprNode getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) }
/**
* Gets an indirect expression node with indirection index `indirectionIndex` that is
@@ -2836,23 +2740,13 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
*
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
*/
Node getAnIndirectBarrierNode(int indirectionIndex) {
IndirectExprNode getAnIndirectBarrierNode(int indirectionIndex) {
exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge |
e = value.getAnInstruction().getConvertedResultExpression() and
result.asIndirectConvertedExpr(indirectionIndex) = e and
result.getConvertedExpr(indirectionIndex) = e and
guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and
g.controls(result.getBasicBlock(), edge)
)
or
exists(
IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi
|
guardChecks(g,
def.getARead().asIndirectOperand(indirectionIndex).getDef().getConvertedResultExpression(),
branch) and
guardControlsPhiInput(g, branch, def, input, phi) and
result = TSsaPhiInputNode(phi, input)
)
}
}
@@ -2861,14 +2755,6 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
*/
signature predicate instructionGuardChecksSig(IRGuardCondition g, Instruction instr, boolean branch);
private EdgeKind getConditionalEdge(boolean branch) {
branch = true and
result instanceof TrueEdge
or
branch = false and
result instanceof FalseEdge
}
/**
* Provides a set of barrier nodes for a guard that validates an instruction.
*
@@ -2877,20 +2763,12 @@ private EdgeKind getConditionalEdge(boolean branch) {
*/
module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardChecks> {
/** Gets a node that is safely guarded by the given guard check. */
Node getABarrierNode() {
ExprNode getABarrierNode() {
exists(IRGuardCondition g, ValueNumber value, boolean edge, Operand use |
instructionGuardChecks(g, value.getAnInstruction(), edge) and
use = value.getAnInstruction().getAUse() and
result.asOperand() = use and
g.controls(result.getBasicBlock(), edge)
)
or
exists(
IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi
|
instructionGuardChecks(g, def.getARead().asOperand().getDef(), branch) and
guardControlsPhiInput(g, branch, def, input, phi) and
result = TSsaPhiInputNode(phi, input)
g.controls(use.getDef().getBlock(), edge)
)
}
}

View File

@@ -546,7 +546,7 @@ module ProductFlow {
Flow1::PathGraph::edges(pred1, succ1, _, _) and
exists(ReturnKindExt returnKind |
succ1.getNode() = returnKind.getAnOutNode(call) and
paramReturnNode(_, pred1.asParameterReturnNode(), _, returnKind)
pred1.getNode().(ReturnNodeExt).getKind() = returnKind
)
}
@@ -574,7 +574,7 @@ module ProductFlow {
Flow2::PathGraph::edges(pred2, succ2, _, _) and
exists(ReturnKindExt returnKind |
succ2.getNode() = returnKind.getAnOutNode(call) and
paramReturnNode(_, pred2.asParameterReturnNode(), _, returnKind)
pred2.getNode().(ReturnNodeExt).getKind() = returnKind
)
}

View File

@@ -657,9 +657,19 @@ class GlobalDefImpl extends DefImpl, TGlobalDefImpl {
*/
predicate adjacentDefRead(IRBlock bb1, int i1, SourceVariable sv, IRBlock bb2, int i2) {
adjacentDefReadExt(_, sv, bb1, i1, bb2, i2)
or
exists(PhiNode phi |
lastRefRedefExt(_, sv, bb1, i1, phi) and
phi.definesAt(sv, bb2, i2, _)
)
}
predicate useToNode(IRBlock bb, int i, SourceVariable sv, Node nodeTo) {
exists(Phi phi |
phi.asPhi().definesAt(sv, bb, i, _) and
nodeTo = phi.getNode()
)
or
exists(UseImpl use |
use.hasIndexInBlock(bb, i, sv) and
nodeTo = use.getNode()
@@ -713,26 +723,46 @@ predicate nodeToDefOrUse(Node node, SourceVariable sv, IRBlock bb, int i, boolea
*/
private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
not exists(SourceVariable sv, IRBlock bb2, int i2 |
useToNode(bb2, i2, sv, nTo) and
nodeToDefOrUse(nTo, sv, bb2, i2, _) and
adjacentDefRead(bb2, i2, sv, _, _)
) and
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and
instr = op2.getDef() and
conversionFlow(op1, instr, _, _)
(
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and
instr = op2.getDef() and
conversionFlow(op1, instr, _, _)
)
or
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
hasOperandAndIndex(nTo, op2, indirectionIndex - 1) and
instr = op2.getDef() and
isDereference(instr, op1, _)
)
)
}
/**
* Holds if `node` is a phi input node that should receive flow from the
* definition to (or use of) `sv` at `(bb1, i1)`.
* The reason for this predicate is a bit annoying:
* We cannot mark a `PointerArithmeticInstruction` that computes an offset based on some SSA
* variable `x` as a use of `x` since this creates taint-flow in the following example:
* ```c
* int x = array[source]
* sink(*array)
* ```
* This is because `source` would flow from the operand of `PointerArithmeticInstruction` to the
* result of the instruction, and into the `IndirectOperand` that represents the value of `*array`.
* Then, via use-use flow, flow will arrive at `*array` in `sink(*array)`.
*
* So this predicate recurses back along conversions and `PointerArithmeticInstruction`s to find the
* first use that has provides use-use flow, and uses that target as the target of the `nodeFrom`.
*/
private predicate phiToNode(SsaPhiInputNode node, SourceVariable sv, IRBlock bb1, int i1) {
exists(PhiNode phi, IRBlock input |
phi.hasInputFromBlock(_, sv, bb1, i1, input) and
node.getPhiNode() = phi and
node.getBlock() = input
private predicate adjustForPointerArith(PostUpdateNode pun, SourceVariable sv, IRBlock bb2, int i2) {
exists(IRBlock bb1, int i1, Node adjusted |
indirectConversionFlowStep*(adjusted, pun.getPreUpdateNode()) and
nodeToDefOrUse(adjusted, sv, bb1, i1, _) and
adjacentDefRead(bb1, i1, sv, bb2, i2)
)
}
@@ -747,14 +777,10 @@ private predicate phiToNode(SsaPhiInputNode node, SourceVariable sv, IRBlock bb1
private predicate ssaFlowImpl(
IRBlock bb1, int i1, SourceVariable sv, Node nodeFrom, Node nodeTo, boolean uncertain
) {
nodeToDefOrUse(nodeFrom, sv, bb1, i1, uncertain) and
(
exists(IRBlock bb2, int i2 |
adjacentDefRead(bb1, i1, sv, bb2, i2) and
useToNode(bb2, i2, sv, nodeTo)
)
or
phiToNode(nodeTo, sv, bb1, i1)
exists(IRBlock bb2, int i2 |
nodeToDefOrUse(nodeFrom, sv, bb1, i1, uncertain) and
adjacentDefRead(bb1, i1, sv, bb2, i2) and
useToNode(bb2, i2, sv, nodeTo)
) and
nodeFrom != nodeTo
}
@@ -763,7 +789,7 @@ private predicate ssaFlowImpl(
private Node getAPriorDefinition(DefinitionExt next) {
exists(IRBlock bb, int i, SourceVariable sv |
lastRefRedefExt(_, pragma[only_bind_into](sv), pragma[only_bind_into](bb),
pragma[only_bind_into](i), _, next) and
pragma[only_bind_into](i), next) and
nodeToDefOrUse(result, sv, bb, i, _)
)
}
@@ -870,31 +896,9 @@ private predicate isArgumentOfCallable(DataFlowCall call, Node n) {
* Holds if there is use-use flow from `pun`'s pre-update node to `n`.
*/
private predicate postUpdateNodeToFirstUse(PostUpdateNode pun, Node n) {
// We cannot mark a `PointerArithmeticInstruction` that computes an offset
// based on some SSA
// variable `x` as a use of `x` since this creates taint-flow in the
// following example:
// ```c
// int x = array[source]
// sink(*array)
// ```
// This is because `source` would flow from the operand of `PointerArithmetic`
// instruction to the result of the instruction, and into the `IndirectOperand`
// that represents the value of `*array`. Then, via use-use flow, flow will
// arrive at `*array` in `sink(*array)`.
// So this predicate recurses back along conversions and `PointerArithmetic`
// instructions to find the first use that has provides use-use flow, and
// uses that target as the target of the `nodeFrom`.
exists(Node adjusted, IRBlock bb1, int i1, SourceVariable sv |
indirectConversionFlowStep*(adjusted, pun.getPreUpdateNode()) and
useToNode(bb1, i1, sv, adjusted)
|
exists(IRBlock bb2, int i2 |
adjacentDefRead(bb1, i1, sv, bb2, i2) and
useToNode(bb2, i2, sv, n)
)
or
phiToNode(n, sv, bb1, i1)
exists(SourceVariable sv, IRBlock bb2, int i2 |
adjustForPointerArith(pun, sv, bb2, i2) and
useToNode(bb2, i2, sv, n)
)
}
@@ -949,16 +953,11 @@ predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
/** Holds if `nodeTo` receives flow from the phi node `nodeFrom`. */
predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1 |
exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, IRBlock bb2, int i2 |
phi = nodeFrom.getPhiNode() and
phi.definesAt(sv, bb1, i1, _)
|
exists(IRBlock bb2, int i2 |
adjacentDefRead(bb1, i1, sv, bb2, i2) and
useToNode(bb2, i2, sv, nodeTo)
)
or
phiToNode(nodeTo, sv, bb1, i1)
phi.definesAt(sv, bb1, i1, _) and
adjacentDefRead(bb1, i1, sv, bb2, i2) and
useToNode(bb2, i2, sv, nodeTo)
)
}
@@ -1032,26 +1031,22 @@ module SsaCached {
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
* `def`. The reference is last because it can reach another write `next`,
* without passing through another read or write.
*
* The path from node `i` in `bb` to `next` goes via basic block `input`,
* which is either a predecessor of the basic block of `next`, or `input` =
* `bb` in case `next` occurs in basic block `bb`.
*/
cached
predicate lastRefRedefExt(
DefinitionExt def, SourceVariable sv, IRBlock bb, int i, IRBlock input, DefinitionExt next
DefinitionExt def, SourceVariable sv, IRBlock bb, int i, DefinitionExt next
) {
SsaImpl::lastRefRedefExt(def, sv, bb, i, input, next)
SsaImpl::lastRefRedefExt(def, sv, bb, i, next)
}
cached
Definition phiHasInputFromBlockExt(PhiNode phi, IRBlock bb) {
SsaImpl::phiHasInputFromBlockExt(phi, result, bb)
Definition phiHasInputFromBlock(PhiNode phi, IRBlock bb) {
SsaImpl::phiHasInputFromBlock(phi, result, bb)
}
cached
predicate ssaDefReachesReadExt(SourceVariable v, DefinitionExt def, IRBlock bb, int i) {
SsaImpl::ssaDefReachesReadExt(v, def, bb, i)
predicate ssaDefReachesRead(SourceVariable v, Definition def, IRBlock bb, int i) {
SsaImpl::ssaDefReachesRead(v, def, bb, i)
}
predicate variableRead = SsaInput::variableRead/4;
@@ -1203,11 +1198,11 @@ class Phi extends TPhi, SsaDef {
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
override string toString() { result = phi.toString() }
override string toString() { result = "Phi" }
SsaPhiInputNode getNode(IRBlock block) { result.getPhiNode() = phi and result.getBlock() = block }
SsaPhiNode getNode() { result.getPhiNode() = phi }
predicate hasInputFromBlock(Definition inp, IRBlock bb) { inp = phiHasInputFromBlockExt(phi, bb) }
predicate hasInputFromBlock(Definition inp, IRBlock bb) { inp = phiHasInputFromBlock(phi, bb) }
final Definition getAnInput() { this.hasInputFromBlock(result, _) }
}
@@ -1233,21 +1228,13 @@ class PhiNode extends SsaImpl::DefinitionExt {
*/
predicate isPhiRead() { this instanceof SsaImpl::PhiReadNode }
/**
* Holds if the node at index `i` in `bb` is a last reference to SSA
* definition `def` of `sv`. The reference is last because it can reach
* this phi node, without passing through another read or write.
*
* The path from node `i` in `bb` to this phi node goes via basic block
* `input`, which is either a predecessor of the basic block of this phi
* node, or `input` = `bb` in case this phi node occurs in basic block `bb`.
*/
predicate hasInputFromBlock(DefinitionExt def, SourceVariable sv, IRBlock bb, int i, IRBlock input) {
SsaCached::lastRefRedefExt(def, sv, bb, i, input, this)
/** Holds if `inp` is an input to this phi node along the edge originating in `bb`. */
predicate hasInputFromBlock(Definition inp, IRBlock bb) {
inp = SsaCached::phiHasInputFromBlock(this, bb)
}
/** Gets a definition that is an input to this phi node. */
final Definition getAnInput() { this.hasInputFromBlock(result, _, _, _, _) }
final Definition getAnInput() { this.hasInputFromBlock(result, _) }
}
/** An static single assignment (SSA) definition. */
@@ -1262,15 +1249,6 @@ class DefinitionExt extends SsaImpl::DefinitionExt {
result = this.getAPhiInputOrPriorDefinition*() and
not result instanceof PhiNode
}
/** Gets a node that represents a read of this SSA definition. */
Node getARead() {
exists(SourceVariable sv, IRBlock bb, int i | SsaCached::ssaDefReachesReadExt(sv, this, bb, i) |
useToNode(bb, i, sv, result)
or
phiToNode(result, sv, bb, i)
)
}
}
class Definition = SsaImpl::Definition;

View File

@@ -1844,6 +1844,9 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr {
child = this.getRightOperand() and
result = this.getLeftOperand().getFirstInstruction(kind)
or
child = this.getRightOperand() and
result = this.getLeftOperand().getFirstInstruction(kind)
or
kind instanceof GotoEdge and
child = this.getLeftOperand() and
result = this.getInstruction(AssignmentStoreTag())
@@ -3208,20 +3211,9 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) }
/**
* Gets the rnk'th (0-indexed) child for which a `TranslatedElement` exists.
*
* We use this predicate to filter out `TypeName` expressions that sometimes
* occur in builtin operations since the IR doesn't have an instruction to
* represent a reference to a type.
*/
private TranslatedElement getRankedChild(int rnk) {
result = rank[rnk + 1](int id, TranslatedElement te | te = this.getChild(id) | te order by id)
}
final override Instruction getFirstInstruction(EdgeKind kind) {
if exists(this.getRankedChild(0))
then result = this.getRankedChild(0).getFirstInstruction(kind)
if exists(this.getChild(0))
then result = this.getChild(0).getFirstInstruction(kind)
else (
kind instanceof GotoEdge and result = this.getInstruction(OnlyInstructionTag())
)
@@ -3241,11 +3233,11 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
}
final override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
exists(int id | child = this.getRankedChild(id) |
result = this.getRankedChild(id + 1).getFirstInstruction(kind)
exists(int id | child = this.getChild(id) |
result = this.getChild(id + 1).getFirstInstruction(kind)
or
kind instanceof GotoEdge and
not exists(this.getRankedChild(id + 1)) and
not exists(this.getChild(id + 1)) and
result = this.getInstruction(OnlyInstructionTag())
)
}
@@ -3260,7 +3252,7 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
tag = OnlyInstructionTag() and
exists(int index |
operandTag = positionalArgumentOperand(index) and
result = this.getRankedChild(index).(TranslatedExpr).getResult()
result = this.getChild(index).(TranslatedExpr).getResult()
)
}

View File

@@ -1,4 +1,4 @@
description: Removed unused column from the `folders` and `files` relations
compatibility: full
files.rel: reorder files.rel (@file id, string name, string simple, string ext, int fromSource) id name
folders.rel: reorder folders.rel (@folder id, string name, string simple) id name
files.rel: reorder files.rel (int id, string name, string simple, string ext, int fromSource) id name
folders.rel: reorder folders.rel (int id, string name, string simple) id name

View File

@@ -1,26 +1,3 @@
## 1.0.1
### Minor Analysis Improvements
* The `cpp/dangerous-function-overflow` no longer produces a false positive alert when the `gets` function does not have exactly one parameter.
## 1.0.0
### Breaking Changes
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
### Minor Analysis Improvements
* The "Use of unique pointer after lifetime ends" query (`cpp/use-of-unique-pointer-after-lifetime-ends`) no longer reports an alert when the pointer is converted to a boolean
* The "Variable not initialized before use" query (`cpp/not-initialised`) no longer reports an alert on static variables.
## 0.9.12
### New Queries
* Added a new query, `cpp/iterator-to-expired-container`, to detect the creation of iterators owned by a temporary objects that are about to be destroyed.
## 0.9.11
### Minor Analysis Improvements

View File

@@ -54,7 +54,6 @@ predicate undefinedLocalUse(VariableAccess va) {
// it is hard to tell when a struct or array has been initialized, so we
// ignore them
not isAggregateType(lv.getUnderlyingType()) and
not lv.isStatic() and // static variables are initialized to zero or null by default
not lv.getType().hasName("va_list") and
va = lv.getAnAccess() and
noDefPath(lv, va) and
@@ -71,8 +70,7 @@ predicate uninitialisedGlobal(GlobalVariable gv) {
va = gv.getAnAccess() and
va.isRValue() and
not gv.hasInitializer() and
not gv.hasSpecifier("extern") and
not gv.isStatic() // static variables are initialized to zero or null by default
not gv.hasSpecifier("extern")
)
}

View File

@@ -42,7 +42,7 @@ in the previous example, one solution is to make the log message a trailing argu
<p>An alternative solution is to allow <code>log_with_timestamp</code> to accept format arguments:</p>
<sample src="NonConstantFormat-2-good.c" />
<p>In this formulation, the non-constant format string to <code>printf</code> has been replaced with
a non-constant format string to <code>vprintf</code>. The analysis will no longer consider the body of
a non-constant format string to <code>vprintf</code>. Semmle will no longer consider the body of
<code>log_with_timestamp</code> to be a problem, and will instead check that every call to
<code>log_with_timestamp</code> passes a constant format string.</p>

View File

@@ -37,19 +37,6 @@ class AllocaCall extends FunctionCall {
}
}
/**
* Gets an expression associated with a dataflow node.
*/
private Expr getExpr(DataFlow::Node node) {
result = node.asInstruction().getAst()
or
result = node.asOperand().getUse().getAst()
or
result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst()
or
result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst()
}
/**
* A loop that contains an `alloca` call.
*/
@@ -198,6 +185,19 @@ class LoopWithAlloca extends Stmt {
not this.conditionReachesWithoutUpdate(var, this.(Loop).getCondition())
}
/**
* Gets an expression associated with a dataflow node.
*/
private Expr getExpr(DataFlow::Node node) {
result = node.asInstruction().getAst()
or
result = node.asOperand().getUse().getAst()
or
result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst()
or
result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst()
}
/**
* Gets a definition that may be the most recent definition of the
* controlling variable `var` before this loop.
@@ -209,9 +209,8 @@ class LoopWithAlloca extends Stmt {
DataFlow::localFlow(result, DataFlow::exprNode(va)) and
// Phi nodes will be preceded by nodes that represent actual definitions
not result instanceof DataFlow::SsaPhiNode and
not result instanceof DataFlow::SsaPhiInputNode and
// A source is outside the loop if it's not inside the loop
not exists(Expr e | e = getExpr(result) | this = getAnEnclosingLoopOfExpr(e))
not exists(Expr e | e = this.getExpr(result) | this = getAnEnclosingLoopOfExpr(e))
)
}
@@ -222,9 +221,9 @@ class LoopWithAlloca extends Stmt {
private int getAControllingVarInitialValue(Variable var, DataFlow::Node source) {
source = this.getAPrecedingDef(var) and
(
result = getExpr(source).getValue().toInt()
result = this.getExpr(source).getValue().toInt()
or
result = getExpr(source).(Assignment).getRValue().getValue().toInt()
result = this.getExpr(source).(Assignment).getRValue().getValue().toInt()
)
}

View File

@@ -107,7 +107,7 @@ class SnprintfSizeExpr extends BufferAccess, FunctionCall {
}
class MemcmpSizeExpr extends BufferAccess, FunctionCall {
MemcmpSizeExpr() { this.getTarget().hasName("memcmp") }
MemcmpSizeExpr() { this.getTarget().hasName("Memcmp") }
override Expr getPointer() {
result = this.getArgument(0) or

View File

@@ -0,0 +1,22 @@
int main(int argc, char** argv) {
char *userAndFile = argv[2];
{
char fileBuffer[FILENAME_MAX] = "/home/";
char *fileName = fileBuffer;
size_t len = strlen(fileName);
strncat(fileName+len, userAndFile, FILENAME_MAX-len-1);
// BAD: a string from the user is used in a filename
fopen(fileName, "wb+");
}
{
char fileBuffer[FILENAME_MAX] = "/home/";
char *fileName = fileBuffer;
size_t len = strlen(fileName);
// GOOD: use a fixed file
char* fixed = "jim/file.txt";
strncat(fileName+len, fixed, FILENAME_MAX-len-1);
fopen(fileName, "wb+");
}
}

View File

@@ -3,57 +3,36 @@
"qhelp.dtd">
<qhelp>
<overview>
<p>Accessing paths controlled by users can allow an attacker to access unexpected resources. This
<p>Accessing paths controlled by users can allow an attacker to access unexpected resources. This
can result in sensitive information being revealed or deleted, or an attacker being able to influence
behavior by modifying unexpected files.</p>
<p>Paths that are naively constructed from data controlled by a user may be absolute paths, or may contain
unexpected special characters such as "..". Such a path could point anywhere on the file system.</p>
<p>Paths that are naively constructed from data controlled by a user may contain unexpected special characters,
such as "..". Such a path may potentially point to any directory on the filesystem.</p>
</overview>
<recommendation>
<p>Validate user input before using it to construct a file path.</p>
<p>Validate user input before using it to construct a filepath. Ideally, follow these rules:</p>
<p>Common validation methods include checking that the normalized path is relative and does not contain
any ".." components, or checking that the path is contained within a safe folder. The method you should use depends
on how the path is used in the application, and whether the path should be a single path component.
</p>
<p>If the path should be a single path component (such as a file name), you can check for the existence
of any path separators ("/" or "\"), or ".." sequences in the input, and reject the input if any are found.
</p>
<p>
Note that removing "../" sequences is <i>not</i> sufficient, since the input could still contain a path separator
followed by "..". For example, the input ".../...//" would still result in the string "../" if only "../" sequences
are removed.
</p>
<p>Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that
the user input matches one of these patterns.</p>
<ul>
<li>Do not allow more than a single "." character.</li>
<li>Do not allow directory separators such as "/" or "\" (depending on the filesystem).</li>
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to
".../...//" the resulting string would still be "../".</li>
<li>Ideally use a whitelist of known good patterns.</li>
</ul>
</recommendation>
<example>
<p>In this example, a file name is read from a user and then used to access a file.
However, a malicious user could enter a file name anywhere on the file system,
such as "/etc/passwd" or "../../../etc/passwd".</p>
<p>In this example, a username and file are read from the arguments to main and then used to access a file in the
user's home directory. However, a malicious user could enter a filename which contains special
characters. For example, the string "../../etc/passwd" will result in the code reading the file located at
"/home/[user]/../../etc/passwd", which is the system's password file. This could potentially allow them to
access all the system's passwords.</p>
<sample src="examples/TaintedPath.c" />
<p>
If the input should only be a file name, you can check that it doesn't contain any path separators or ".." sequences.
</p>
<sample src="examples/TaintedPathNormalize.c" />
<p>
If the input should be within a specific directory, you can check that the resolved path
is still contained within that directory.
</p>
<sample src="examples/TaintedPathFolder.c" />
<sample src="TaintedPath.c" />
</example>
<references>
@@ -62,7 +41,6 @@ is still contained within that directory.
OWASP:
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.
</li>
<li>Linux man pages: <a href="https://man7.org/linux/man-pages/man3/realpath.3.html">realpath(3)</a>.</li>
</references>
</qhelp>

View File

@@ -1,10 +0,0 @@
int main(int argc, char** argv) {
char *userAndFile = argv[2];
{
char fileBuffer[PATH_MAX];
snprintf(fileBuffer, sizeof(fileBuffer), "/home/%s", userAndFile);
// BAD: a string from the user is used in a filename
fopen(fileBuffer, "wb+");
}
}

View File

@@ -1,28 +0,0 @@
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv) {
char *userAndFile = argv[2];
const char *baseDir = "/home/user/public/";
char fullPath[PATH_MAX];
// Attempt to concatenate the base directory and the user-supplied path
snprintf(fullPath, sizeof(fullPath), "%s%s", baseDir, userAndFile);
// Resolve the absolute path, normalizing any ".." or "."
char *resolvedPath = realpath(fullPath, NULL);
if (resolvedPath == NULL) {
perror("Error resolving path");
return 1;
}
// Check if the resolved path starts with the base directory
if (strncmp(baseDir, resolvedPath, strlen(baseDir)) != 0) {
free(resolvedPath);
return 1;
}
// GOOD: Path is within the intended directory
FILE *file = fopen(resolvedPath, "wb+");
free(resolvedPath);
}

View File

@@ -1,16 +0,0 @@
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv) {
char *fileName = argv[2];
// Check for invalid sequences in the user input
if (strstr(fileName , "..") || strchr(fileName , '/') || strchr(fileName , '\\')) {
printf("Invalid filename.\n");
return 1;
}
char fileBuffer[PATH_MAX];
snprintf(fileBuffer, sizeof(fileBuffer), "/home/user/files/%s", fileName);
// GOOD: We know that the filename is safe and stays within the public folder
FILE *file = fopen(fileBuffer, "wb+");
}

View File

@@ -12,8 +12,8 @@ the required buffer size, but do not allocate space for the zero terminator.
</overview>
<recommendation>
<p>
The highlighted code segment creates a buffer without ensuring it's large enough to accommodate the copied data.
This leaves the code susceptible to a buffer overflow attack, which could lead to anything from program crashes to malicious code execution.
The expression highlighted by this rule creates a buffer that is of insufficient size to contain
the data being copied. This makes the code vulnerable to buffer overflow which can result in anything from a segmentation fault to a security vulnerability (particularly if the array is on stack-allocated memory).
</p>
<p>

View File

@@ -30,8 +30,6 @@ where
outlivesFullExpr(c) and
not c.isFromUninstantiatedTemplate(_) and
isUniquePointerDerefFunction(c.getTarget()) and
// Exclude cases where the pointer is implicitly converted to a non-pointer type
not c.getActualType() instanceof IntegralType and
isTemporary(c.getQualifier().getFullyConverted())
select c,
"The underlying unique pointer object is destroyed after the call to '" + c.getTarget() +

View File

@@ -17,6 +17,5 @@ import cpp
from FunctionCall call, Function target
where
call.getTarget() = target and
target.hasGlobalOrStdName("gets") and
target.getNumberOfParameters() = 1
target.hasGlobalOrStdName("gets")
select call, "'gets' does not guard against buffer overflow."

View File

@@ -1,5 +1,4 @@
## 0.9.12
### New Queries
---
category: newQuery
---
* Added a new query, `cpp/iterator-to-expired-container`, to detect the creation of iterators owned by a temporary objects that are about to be destroyed.

View File

@@ -1,10 +0,0 @@
## 1.0.0
### Breaking Changes
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
### Minor Analysis Improvements
* The "Use of unique pointer after lifetime ends" query (`cpp/use-of-unique-pointer-after-lifetime-ends`) no longer reports an alert when the pointer is converted to a boolean
* The "Variable not initialized before use" query (`cpp/not-initialised`) no longer reports an alert on static variables.

View File

@@ -1,5 +0,0 @@
## 1.0.1
### Minor Analysis Improvements
* The `cpp/dangerous-function-overflow` no longer produces a false positive alert when the `gets` function does not have exactly one parameter.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.1
lastReleaseVersion: 0.9.11

View File

@@ -1,23 +0,0 @@
char * create (int arg) {
if (arg > 42) {
// this function may return NULL
return NULL;
}
char * r = malloc(arg);
snprintf(r, arg -1, "Hello");
return r;
}
void process(char *str) {
// str is dereferenced
if (str[0] == 'H') {
printf("Hello H\n");
}
}
void test(int arg) {
// first function returns a pointer that may be NULL
char *str = create(arg);
// str is not checked for nullness before being passed to process function
process(str);
}

View File

@@ -1,26 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds a dereference of a function parameter, whose value comes from another function call that may return NULL, without checks in the meantime.</p>
</overview>
<recommendation>
<p>A check should be added between the return of the function which may return NULL, and its use by the function dereferencing ths pointer.</p>
</recommendation>
<example>
<sample src="DerefNullResult.cpp" />
</example>
<references>
<li>
<a href="https://www.owasp.org/index.php/Null_Dereference">
Null Dereference
</a>
</li>
</references>
</qhelp>

View File

@@ -1,34 +0,0 @@
/**
* @name Null dereference from a function result
* @description A function parameter is dereferenced,
* while it comes from a function that may return NULL,
* and is not checked for nullness by the caller.
* @kind problem
* @id cpp/deref-null-result
* @problem.severity recommendation
* @tags reliability
* security
* external/cwe/cwe-476
*/
import cpp
import semmle.code.cpp.dataflow.new.DataFlow
from Function nuller, Parameter pd, FunctionCall fc, Variable v
where
mayReturnNull(nuller) and
functionDereferences(pd.getFunction(), pd.getIndex()) and
// there is a function call which will deref parameter pd
fc.getTarget() = pd.getFunction() and
// the parameter pd comes from a variable v
DataFlow::localFlow(DataFlow::exprNode(v.getAnAccess()),
DataFlow::exprNode(fc.getArgument(pd.getIndex()))) and
// this variable v was assigned by a call to the nuller function
unique( | | v.getAnAssignedValue()) = nuller.getACallToThisFunction() and
// this variable v is not accessed for an operation (check for NULLness)
not exists(VariableAccess vc |
vc.getTarget() = v and
(vc.getParent() instanceof Operation or vc.getParent() instanceof IfStmt)
)
select fc, "This function call may deref $@ when it can be NULL from $@", v, v.getName(), nuller,
nuller.getName()

View File

@@ -0,0 +1,20 @@
import cpp
class StringVariable extends Variable {
StringVariable() {
this.getType().(PointerType).stripType().getName() = "char"
}
}
class StringField extends StringVariable, Field
{
}
class StringParameter extends StringVariable, Parameter
{
}
from StringVariable f
where f.getFile().getRelativePath().matches("c/extractor/src/%")
and not f.getASpecifier().getName() = "extern"
select f, f.getFile().getRelativePath()

View File

@@ -0,0 +1,38 @@
import relevant
/*
A "class" is an island of globals and functions where all globals and functions are connected
to each other.
*/
class ExtractedClass extends RelevantGlobalVariable
{
ExtractedClass()
{
any()
}
Function getAFunction()
{
result = this.getAnAccess().getEnclosingFunction()
}
}
predicate reaches(GlobalVariable v1, GlobalVariable v2)
{
exists(Function fn | v1.getAnAccess().getEnclosingFunction() = fn and
v2.getAnAccess().getEnclosingFunction() = fn)
}
predicate primary(RelevantGlobalVariable v1)
{
exists(RelevantGlobalVariable v2 | reaches(v1, v2)) and
not exists(RelevantGlobalVariable v2 | reaches*(v1, v2) | v1.getName() < v2.getName())
}
from RelevantGlobalVariable var
where primary(var)
select var, "This is a primary global variable"

View File

@@ -0,0 +1,9 @@
import cpp
import relevant
from Function fn
where
fn.getNamespace() instanceof GlobalNamespace and
not exists(fn.getDeclaringType()) and
is_relevant_result(fn.getFile())
select fn, "This function is not declared in a namespace", fn.getFile().getRelativePath()

View File

@@ -0,0 +1,4 @@
import cpp
from Function fn
where not exists(fn.getDeclaringType()) and is_relevant_result(fn.getFile())

View File

@@ -0,0 +1,10 @@
// Flags use of global variables
import cpp
import relevant
from RelevantGlobalVariable globalVariable, string typeName, Function fn, VariableAccess va
where
typeName = globalVariable.getType().stripType().getName()
and fn = globalVariable.getAnAccess().getEnclosingFunction()
select globalVariable, fn

View File

@@ -0,0 +1,9 @@
// Flags use of global variables
import cpp
import relevant
from RelevantGlobalVariable globalVariable, string typeName
where
typeName = globalVariable.getType().stripType().getName()
select globalVariable, typeName, globalVariable.getFile().getRelativePath()

View File

@@ -0,0 +1,7 @@
import cpp
import relevant
from Call call
where call.getTarget().getName().matches("%printf")
and is_relevant_result(call.getFile())
select call, "Call to a printf formatter", call.getFile().getRelativePath()

View File

@@ -0,0 +1,25 @@
import cpp
predicate is_relevant_result(File file)
{
not file.getRelativePath().matches("c/extractor/edg%")
}
class RelevantGlobalVariable extends GlobalVariable
{
RelevantGlobalVariable() {
not is_valid_global_variable(this) and
exists(this.getFile().getRelativePath()) // From the repo
}
}
predicate is_valid_global_variable(Variable var) {
var.getType().stripType().getName() = "trie_node" or
var.getType().isConst() or
var.getType().isDeeplyConst() or
var.isConstexpr() or
// var.getType() instanceof ArrayType or
var.getASpecifier().getName() = "extern" or
var.getFile().getRelativePath().matches("c/extractor/edg/%") // or
// var.getFile().getRelativePath().matches("c/extractor/edg%") or
}

View File

@@ -0,0 +1,7 @@
import relevant
from Function fn, StaticLocalVariable var, ReturnStmt ret, string path
where ret.getExpr() = var.getAnAccess()
and var.getFunction() = fn
and path = fn.getFile().getRelativePath()
select fn, "This function returns a static local variable"

View File

@@ -0,0 +1,25 @@
import cpp
class ACompressedFileWrite extends Function {
ACompressedFileWrite() {
this.getName() = "operator<<" and
this.getParameter(0).getType().stripType().getName() = "a_compressed_file"
}
}
class LabelDefinition extends Call {
LabelDefinition() {
this.getTarget() instanceof ACompressedFileWrite and
this.getArgument(1).(StringLiteral).getValue().matches("=%")
}
}
predicate is_valid_file_write(Call call) {
call.getFile().getBaseName() = "dbscheme.cpp"
}
from Call call
where
call.getTarget() instanceof ACompressedFileWrite
and not is_valid_file_write(call)
select call

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 1.0.1
version: 0.9.12-dev
groups:
- cpp
- queries

View File

@@ -1,55 +1,55 @@
edges
| test.cpp:34:10:34:12 | buf | test.cpp:34:5:34:24 | access to array | provenance | Config |
| test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | provenance | Config |
| test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | provenance | Config |
| test.cpp:39:14:39:16 | buf | test.cpp:39:9:39:19 | access to array | provenance | Config |
| test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | provenance | Config |
| test.cpp:48:10:48:12 | buf | test.cpp:48:5:48:24 | access to array | provenance | Config |
| test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | provenance | Config |
| test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | provenance | Config |
| test.cpp:53:14:53:16 | buf | test.cpp:53:9:53:19 | access to array | provenance | Config |
| test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | provenance | Config |
| test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | provenance | Config |
| test.cpp:70:33:70:33 | p | test.cpp:71:5:71:17 | access to array | provenance | Config |
| test.cpp:70:33:70:33 | p | test.cpp:72:5:72:15 | access to array | provenance | Config |
| test.cpp:34:10:34:12 | buf | test.cpp:34:5:34:24 | access to array | provenance | |
| test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | provenance | |
| test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | provenance | |
| test.cpp:39:14:39:16 | buf | test.cpp:39:9:39:19 | access to array | provenance | |
| test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | provenance | |
| test.cpp:48:10:48:12 | buf | test.cpp:48:5:48:24 | access to array | provenance | |
| test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | provenance | |
| test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | provenance | |
| test.cpp:53:14:53:16 | buf | test.cpp:53:9:53:19 | access to array | provenance | |
| test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | provenance | |
| test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | provenance | |
| test.cpp:70:33:70:33 | p | test.cpp:71:5:71:17 | access to array | provenance | |
| test.cpp:70:33:70:33 | p | test.cpp:72:5:72:15 | access to array | provenance | |
| test.cpp:76:26:76:46 | & ... | test.cpp:66:32:66:32 | p | provenance | |
| test.cpp:76:32:76:34 | buf | test.cpp:76:26:76:46 | & ... | provenance | Config |
| test.cpp:76:32:76:34 | buf | test.cpp:76:26:76:46 | & ... | provenance | |
| test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | provenance | |
| test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | provenance | Config |
| test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | provenance | |
| test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | provenance | |
| test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | provenance | |
| test.cpp:85:21:85:36 | buf | test.cpp:87:5:87:31 | access to array | provenance | Config |
| test.cpp:85:21:85:36 | buf | test.cpp:88:5:88:27 | access to array | provenance | Config |
| test.cpp:85:21:85:36 | buf | test.cpp:87:5:87:31 | access to array | provenance | |
| test.cpp:85:21:85:36 | buf | test.cpp:88:5:88:27 | access to array | provenance | |
| test.cpp:85:34:85:36 | buf | test.cpp:85:21:85:36 | buf | provenance | |
| test.cpp:96:13:96:15 | arr | test.cpp:96:13:96:18 | access to array | provenance | Config |
| test.cpp:111:17:111:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | Config |
| test.cpp:111:17:111:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | Config |
| test.cpp:111:17:111:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | Config |
| test.cpp:115:35:115:37 | arr | test.cpp:111:17:111:22 | access to array | provenance | Config |
| test.cpp:115:35:115:37 | arr | test.cpp:115:35:115:40 | access to array | provenance | Config |
| test.cpp:115:35:115:37 | arr | test.cpp:119:17:119:22 | access to array | provenance | Config |
| test.cpp:119:17:119:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | Config |
| test.cpp:119:17:119:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | Config |
| test.cpp:119:17:119:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | Config |
| test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | provenance | Config |
| test.cpp:134:25:134:27 | arr | test.cpp:136:9:136:16 | ... += ... | provenance | Config |
| test.cpp:96:13:96:15 | arr | test.cpp:96:13:96:18 | access to array | provenance | |
| test.cpp:111:17:111:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | |
| test.cpp:111:17:111:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | |
| test.cpp:111:17:111:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | |
| test.cpp:115:35:115:37 | arr | test.cpp:111:17:111:22 | access to array | provenance | |
| test.cpp:115:35:115:37 | arr | test.cpp:115:35:115:40 | access to array | provenance | |
| test.cpp:115:35:115:37 | arr | test.cpp:119:17:119:22 | access to array | provenance | |
| test.cpp:119:17:119:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | |
| test.cpp:119:17:119:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | |
| test.cpp:119:17:119:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | |
| test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | provenance | |
| test.cpp:134:25:134:27 | arr | test.cpp:136:9:136:16 | ... += ... | provenance | |
| test.cpp:136:9:136:16 | ... += ... | test.cpp:136:9:136:16 | ... += ... | provenance | |
| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr | provenance | |
| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr | provenance | |
| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf | provenance | |
| test.cpp:146:26:146:26 | *p | test.cpp:147:4:147:9 | -- ... | provenance | |
| test.cpp:156:12:156:14 | buf | test.cpp:156:12:156:18 | ... + ... | provenance | Config |
| test.cpp:156:12:156:14 | buf | test.cpp:156:12:156:18 | ... + ... | provenance | |
| test.cpp:156:12:156:18 | ... + ... | test.cpp:156:12:156:18 | ... + ... | provenance | |
| test.cpp:156:12:156:18 | ... + ... | test.cpp:158:17:158:18 | *& ... | provenance | |
| test.cpp:158:17:158:18 | *& ... | test.cpp:146:26:146:26 | *p | provenance | |
| test.cpp:218:16:218:28 | buffer | test.cpp:220:5:220:11 | access to array | provenance | Config |
| test.cpp:218:16:218:28 | buffer | test.cpp:221:5:221:11 | access to array | provenance | Config |
| test.cpp:218:16:218:28 | buffer | test.cpp:220:5:220:11 | access to array | provenance | |
| test.cpp:218:16:218:28 | buffer | test.cpp:221:5:221:11 | access to array | provenance | |
| test.cpp:218:23:218:28 | buffer | test.cpp:218:16:218:28 | buffer | provenance | |
| test.cpp:229:17:229:29 | array | test.cpp:231:5:231:10 | access to array | provenance | Config |
| test.cpp:229:17:229:29 | array | test.cpp:232:5:232:10 | access to array | provenance | Config |
| test.cpp:229:17:229:29 | array | test.cpp:231:5:231:10 | access to array | provenance | |
| test.cpp:229:17:229:29 | array | test.cpp:232:5:232:10 | access to array | provenance | |
| test.cpp:229:25:229:29 | array | test.cpp:229:17:229:29 | array | provenance | |
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | Config |
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | Config |
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | |
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | |
| test.cpp:274:14:274:20 | buffer3 | test.cpp:245:30:245:30 | p | provenance | |
| test.cpp:274:14:274:20 | buffer3 | test.cpp:274:14:274:20 | buffer3 | provenance | |
| test.cpp:277:35:277:35 | p | test.cpp:278:14:278:14 | p | provenance | |
@@ -60,20 +60,21 @@ edges
| test.cpp:286:19:286:25 | buffer2 | test.cpp:286:19:286:25 | buffer2 | provenance | |
| test.cpp:289:19:289:25 | buffer3 | test.cpp:277:35:277:35 | p | provenance | |
| test.cpp:289:19:289:25 | buffer3 | test.cpp:289:19:289:25 | buffer3 | provenance | |
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | provenance | Config |
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | provenance | |
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | provenance | |
| test.cpp:306:20:306:23 | arr1 | test.cpp:292:25:292:27 | arr | provenance | |
| test.cpp:306:20:306:23 | arr1 | test.cpp:306:20:306:23 | arr1 | provenance | |
| test.cpp:309:20:309:23 | arr2 | test.cpp:292:25:292:27 | arr | provenance | |
| test.cpp:309:20:309:23 | arr2 | test.cpp:309:20:309:23 | arr2 | provenance | |
| test.cpp:319:13:319:27 | ... = ... | test.cpp:325:24:325:26 | end | provenance | |
| test.cpp:319:19:319:22 | temp | test.cpp:319:19:319:27 | ... + ... | provenance | Config |
| test.cpp:319:19:319:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | Config |
| test.cpp:319:19:319:22 | temp | test.cpp:319:19:319:27 | ... + ... | provenance | |
| test.cpp:319:19:319:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
| test.cpp:319:19:319:27 | ... + ... | test.cpp:319:13:319:27 | ... = ... | provenance | |
| test.cpp:322:13:322:27 | ... = ... | test.cpp:325:24:325:26 | end | provenance | |
| test.cpp:322:19:322:22 | temp | test.cpp:322:19:322:27 | ... + ... | provenance | Config |
| test.cpp:322:19:322:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | Config |
| test.cpp:322:19:322:22 | temp | test.cpp:322:19:322:27 | ... + ... | provenance | |
| test.cpp:322:19:322:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
| test.cpp:322:19:322:27 | ... + ... | test.cpp:322:13:322:27 | ... = ... | provenance | |
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | Config |
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
| test.cpp:324:23:324:32 | ... + ... | test.cpp:324:23:324:32 | ... + ... | provenance | |
| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 | provenance | |
nodes
@@ -158,6 +159,7 @@ nodes
| test.cpp:289:19:289:25 | buffer3 | semmle.label | buffer3 |
| test.cpp:289:19:289:25 | buffer3 | semmle.label | buffer3 |
| test.cpp:292:25:292:27 | arr | semmle.label | arr |
| test.cpp:292:25:292:27 | arr | semmle.label | arr |
| test.cpp:299:16:299:21 | access to array | semmle.label | access to array |
| test.cpp:306:20:306:23 | arr1 | semmle.label | arr1 |
| test.cpp:306:20:306:23 | arr1 | semmle.label | arr1 |

View File

@@ -74,8 +74,6 @@ astGuardsCompare
| 34 | j >= 10+0 when ... < ... is false |
| 42 | 10 < j+1 when ... < ... is false |
| 42 | 10 >= j+1 when ... < ... is true |
| 42 | call to getABool != 0 when call to getABool is true |
| 42 | call to getABool == 0 when call to getABool is false |
| 42 | j < 10+0 when ... < ... is true |
| 42 | j >= 10+0 when ... < ... is false |
| 44 | 0 < z+0 when ... > ... is true |
@@ -162,9 +160,6 @@ astGuardsCompare
| 137 | 0 == 0 when 0 is false |
| 146 | ! ... != 0 when ! ... is true |
| 146 | ! ... == 0 when ! ... is false |
| 146 | x != 0 when ! ... is false |
| 146 | x != 0 when x is true |
| 146 | x == 0 when x is false |
| 152 | x != 0 when ... && ... is true |
| 152 | x != 0 when x is true |
| 152 | x == 0 when x is false |
@@ -523,7 +518,6 @@ astGuardsEnsure_const
| test.c:131:7:131:7 | b | test.c:131:7:131:7 | b | != | 0 | 131 | 132 |
| test.c:137:7:137:7 | 0 | test.c:137:7:137:7 | 0 | == | 0 | 142 | 136 |
| test.c:146:7:146:8 | ! ... | test.c:146:7:146:8 | ! ... | != | 0 | 146 | 147 |
| test.c:146:8:146:8 | x | test.c:146:8:146:8 | x | == | 0 | 146 | 147 |
| test.c:152:10:152:10 | x | test.c:152:10:152:10 | x | != | 0 | 151 | 152 |
| test.c:152:10:152:10 | x | test.c:152:10:152:10 | x | != | 0 | 152 | 152 |
| test.c:152:10:152:15 | ... && ... | test.c:152:10:152:10 | x | != | 0 | 151 | 152 |
@@ -539,8 +533,6 @@ astGuardsEnsure_const
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 31 | 32 |
| test.cpp:42:13:42:20 | call to getABool | test.cpp:42:13:42:20 | call to getABool | != | 0 | 43 | 45 |
| test.cpp:42:13:42:20 | call to getABool | test.cpp:42:13:42:20 | call to getABool | == | 0 | 53 | 53 |
irGuards
| test.c:7:9:7:13 | CompareGT: ... > ... |
| test.c:17:8:17:12 | CompareLT: ... < ... |
@@ -617,8 +609,6 @@ irGuardsCompare
| 34 | j >= 10+0 when CompareLT: ... < ... is false |
| 42 | 10 < j+1 when CompareLT: ... < ... is false |
| 42 | 10 >= j+1 when CompareLT: ... < ... is true |
| 42 | call to getABool != 0 when Call: call to getABool is true |
| 42 | call to getABool == 0 when Call: call to getABool is false |
| 42 | j < 10 when CompareLT: ... < ... is true |
| 42 | j < 10+0 when CompareLT: ... < ... is true |
| 42 | j >= 10 when CompareLT: ... < ... is false |
@@ -699,9 +689,6 @@ irGuardsCompare
| 137 | 0 == 0 when Constant: 0 is false |
| 146 | ! ... != 0 when LogicalNot: ! ... is true |
| 146 | ! ... == 0 when LogicalNot: ! ... is false |
| 146 | x != 0 when Load: x is true |
| 146 | x != 0 when LogicalNot: ! ... is false |
| 146 | x == 0 when Load: x is false |
| 152 | x != 0 when Load: x is true |
| 152 | x == 0 when Load: x is false |
| 152 | y != 0 when Load: y is true |
@@ -1076,7 +1063,6 @@ irGuardsEnsure_const
| test.c:131:7:131:7 | Load: b | test.c:131:7:131:7 | Load: b | != | 0 | 132 | 132 |
| test.c:137:7:137:7 | Constant: 0 | test.c:137:7:137:7 | Constant: 0 | == | 0 | 142 | 142 |
| test.c:146:7:146:8 | LogicalNot: ! ... | test.c:146:7:146:8 | LogicalNot: ! ... | != | 0 | 147 | 147 |
| test.c:146:8:146:8 | Load: x | test.c:146:8:146:8 | Load: x | == | 0 | 147 | 147 |
| test.c:152:10:152:10 | Load: x | test.c:152:10:152:10 | Load: x | != | 0 | 152 | 152 |
| test.c:152:15:152:15 | Load: y | test.c:152:15:152:15 | Load: y | != | 0 | 152 | 152 |
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | != | 0 | 175 | 175 |
@@ -1087,5 +1073,3 @@ irGuardsEnsure_const
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | -1 | 34 | 34 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 30 | 30 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 32 | 32 |
| test.cpp:42:13:42:20 | Call: call to getABool | test.cpp:42:13:42:20 | Call: call to getABool | != | 0 | 44 | 44 |
| test.cpp:42:13:42:20 | Call: call to getABool | test.cpp:42:13:42:20 | Call: call to getABool | == | 0 | 53 | 53 |

View File

@@ -42,6 +42,3 @@
| test.cpp:99:6:99:6 | f |
| test.cpp:105:6:105:14 | ... != ... |
| test.cpp:111:6:111:14 | ... != ... |
| test.cpp:122:9:122:9 | b |
| test.cpp:125:13:125:20 | ! ... |
| test.cpp:125:14:125:17 | call to safe |

View File

@@ -44,8 +44,6 @@
| 34 | j >= 10+0 when ... < ... is false |
| 42 | 10 < j+1 when ... < ... is false |
| 42 | 10 >= j+1 when ... < ... is true |
| 42 | call to getABool != 0 when call to getABool is true |
| 42 | call to getABool == 0 when call to getABool is false |
| 42 | j < 10 when ... < ... is true |
| 42 | j < 10+0 when ... < ... is true |
| 42 | j >= 10 when ... < ... is false |
@@ -151,13 +149,6 @@
| 111 | 0.0 == i+0 when ... != ... is false |
| 111 | i != 0.0+0 when ... != ... is true |
| 111 | i == 0.0+0 when ... != ... is false |
| 122 | b != 0 when b is true |
| 122 | b == 0 when b is false |
| 125 | ! ... != 0 when ! ... is true |
| 125 | ! ... == 0 when ! ... is false |
| 125 | call to safe != 0 when ! ... is false |
| 125 | call to safe != 0 when call to safe is true |
| 125 | call to safe == 0 when call to safe is false |
| 126 | 1 != 0 when 1 is true |
| 126 | 1 != 0 when ... && ... is true |
| 126 | 1 == 0 when 1 is false |
@@ -170,20 +161,11 @@
| 137 | 0 == 0 when 0 is false |
| 146 | ! ... != 0 when ! ... is true |
| 146 | ! ... == 0 when ! ... is false |
| 146 | x != 0 when ! ... is false |
| 146 | x != 0 when x is true |
| 146 | x == 0 when x is false |
| 152 | p != 0 when p is true |
| 152 | p == 0 when p is false |
| 158 | ! ... != 0 when ! ... is true |
| 158 | ! ... == 0 when ! ... is false |
| 158 | p != 0 when ! ... is false |
| 158 | p != 0 when p is true |
| 158 | p == 0 when p is false |
| 164 | s != 0 when s is true |
| 164 | s == 0 when s is false |
| 170 | ! ... != 0 when ! ... is true |
| 170 | ! ... == 0 when ! ... is false |
| 170 | s != 0 when ! ... is false |
| 170 | s != 0 when s is true |
| 170 | s == 0 when s is false |

View File

@@ -100,7 +100,3 @@
| test.cpp:99:6:99:6 | f | true | 99 | 100 |
| test.cpp:105:6:105:14 | ... != ... | true | 105 | 106 |
| test.cpp:111:6:111:14 | ... != ... | true | 111 | 112 |
| test.cpp:122:9:122:9 | b | true | 123 | 125 |
| test.cpp:122:9:122:9 | b | true | 125 | 125 |
| test.cpp:125:13:125:20 | ! ... | true | 125 | 125 |
| test.cpp:125:14:125:17 | call to safe | false | 125 | 125 |

View File

@@ -245,20 +245,15 @@ unary
| test.c:131:7:131:7 | b | test.c:131:7:131:7 | b | != | 0 | 131 | 132 |
| test.c:137:7:137:7 | 0 | test.c:137:7:137:7 | 0 | == | 0 | 142 | 136 |
| test.c:146:7:146:8 | ! ... | test.c:146:7:146:8 | ! ... | != | 0 | 146 | 147 |
| test.c:146:8:146:8 | x | test.c:146:8:146:8 | x | == | 0 | 146 | 147 |
| test.c:152:8:152:8 | p | test.c:152:8:152:8 | p | != | 0 | 152 | 154 |
| test.c:158:8:158:9 | ! ... | test.c:158:8:158:9 | ! ... | != | 0 | 158 | 160 |
| test.c:158:9:158:9 | p | test.c:158:9:158:9 | p | == | 0 | 158 | 160 |
| test.c:164:8:164:8 | s | test.c:164:8:164:8 | s | != | 0 | 164 | 166 |
| test.c:170:8:170:9 | ! ... | test.c:170:8:170:9 | ! ... | != | 0 | 170 | 172 |
| test.c:170:9:170:9 | s | test.c:170:9:170:9 | s | == | 0 | 170 | 172 |
| test.cpp:18:8:18:10 | call to get | test.cpp:18:8:18:10 | call to get | != | 0 | 19 | 19 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 31 | 32 |
| test.cpp:42:13:42:20 | call to getABool | test.cpp:42:13:42:20 | call to getABool | != | 0 | 43 | 45 |
| test.cpp:42:13:42:20 | call to getABool | test.cpp:42:13:42:20 | call to getABool | == | 0 | 53 | 53 |
| test.cpp:61:10:61:10 | i | test.cpp:61:10:61:10 | i | == | 0 | 62 | 64 |
| test.cpp:61:10:61:10 | i | test.cpp:61:10:61:10 | i | == | 1 | 65 | 66 |
| test.cpp:74:10:74:10 | i | test.cpp:74:10:74:10 | i | < | 11 | 75 | 77 |
@@ -266,7 +261,3 @@ unary
| test.cpp:74:10:74:10 | i | test.cpp:74:10:74:10 | i | >= | 0 | 75 | 77 |
| test.cpp:74:10:74:10 | i | test.cpp:74:10:74:10 | i | >= | 11 | 78 | 79 |
| test.cpp:93:6:93:6 | c | test.cpp:93:6:93:6 | c | != | 0 | 93 | 94 |
| test.cpp:122:9:122:9 | b | test.cpp:122:9:122:9 | b | != | 0 | 123 | 125 |
| test.cpp:122:9:122:9 | b | test.cpp:122:9:122:9 | b | != | 0 | 125 | 125 |
| test.cpp:125:13:125:20 | ! ... | test.cpp:125:13:125:20 | ! ... | != | 0 | 125 | 125 |
| test.cpp:125:14:125:17 | call to safe | test.cpp:125:14:125:17 | call to safe | == | 0 | 125 | 125 |

View File

@@ -112,17 +112,3 @@ void int_float_comparison(int i) {
use(i);
}
}
int source();
bool safe(int);
void test(bool b)
{
int x;
if (b)
{
x = source();
if (!safe(x)) return;
}
use(x);
}

View File

@@ -75,54 +75,4 @@ void bg_indirect_expr() {
if (guarded(buf)) {
sink(buf);
}
}
void test_guard_and_reassign() {
int x = source();
if(!guarded(x)) {
x = 0;
}
sink(x); // $ SPURIOUS: ast
}
void test_phi_read_guard(bool b) {
int x = source();
if(b) {
if(!guarded(x))
return;
}
else {
if(!guarded(x))
return;
}
sink(x); // $ SPURIOUS: ast
}
bool unsafe(int);
void test_guard_and_reassign_2() {
int x = source();
if(unsafe(x)) {
x = 0;
}
sink(x); // $ SPURIOUS: ast
}
void test_phi_read_guard_2(bool b) {
int x = source();
if(b) {
if(unsafe(x))
return;
}
else {
if(unsafe(x))
return;
}
sink(x); // $ SPURIOUS: ast
}

View File

@@ -7,17 +7,10 @@ module AstTest {
* S in `if (guarded(x)) S`.
*/
// This is tested in `BarrierGuard.cpp`.
predicate testBarrierGuard(GuardCondition g, Expr checked, boolean branch) {
exists(Call call, boolean b |
checked = call.getArgument(0) and
g.comparesEq(call, 0, b, any(BooleanValue bv | bv.getValue() = branch))
|
call.getTarget().hasName("guarded") and
b = false
or
call.getTarget().hasName("unsafe") and
b = true
)
predicate testBarrierGuard(GuardCondition g, Expr checked, boolean isTrue) {
g.(FunctionCall).getTarget().getName() = "guarded" and
checked = g.(FunctionCall).getArgument(0) and
isTrue = true
}
/** Common data flow configuration to be used by tests. */
@@ -109,16 +102,12 @@ module IRTest {
* S in `if (guarded(x)) S`.
*/
// This is tested in `BarrierGuard.cpp`.
predicate testBarrierGuard(IRGuardCondition g, Expr checked, boolean branch) {
exists(CallInstruction call, boolean b |
checked = call.getArgument(0).getUnconvertedResultExpression() and
g.comparesEq(call.getAUse(), 0, b, any(BooleanValue bv | bv.getValue() = branch))
|
call.getStaticCallTarget().hasName("guarded") and
b = false
or
call.getStaticCallTarget().hasName("unsafe") and
b = true
predicate testBarrierGuard(IRGuardCondition g, Expr checked, boolean isTrue) {
exists(Call call |
call = g.getUnconvertedResultExpression() and
call.getTarget().hasName("guarded") and
checked = call.getArgument(0) and
isTrue = true
)
}

View File

@@ -52,9 +52,3 @@ void following_pointers( // $ ast-def=sourceStruct1_ptr ir-def=*cleanArray1 ir-d
sink(stackArray); // $ ast,ir
indirect_sink(stackArray); // $ ast ir=50:25 ir=50:35 ir=51:19
}
void test_bitcast() {
unsigned long x = source();
double d = __builtin_bit_cast(double, x);
sink(d); // $ ir MISSING: ast
}

View File

@@ -69,61 +69,45 @@
| test.cpp:8:8:8:9 | t1 | test.cpp:9:8:9:9 | t1 |
| test.cpp:9:8:9:9 | t1 | test.cpp:11:7:11:8 | t1 |
| test.cpp:9:8:9:9 | t1 | test.cpp:11:7:11:8 | t1 |
| test.cpp:10:8:10:9 | t2 | test.cpp:11:7:11:8 | Phi input |
| test.cpp:10:8:10:9 | t2 | test.cpp:11:7:11:8 | Phi input |
| test.cpp:10:8:10:9 | t2 | test.cpp:13:10:13:11 | t2 |
| test.cpp:11:7:11:8 | Phi input | test.cpp:15:3:15:6 | SSA phi read(t2) |
| test.cpp:11:7:11:8 | Phi input | test.cpp:15:3:15:6 | SSA phi(*t2) |
| test.cpp:10:8:10:9 | t2 | test.cpp:15:3:15:6 | Phi |
| test.cpp:10:8:10:9 | t2 | test.cpp:15:3:15:6 | Phi |
| test.cpp:11:7:11:8 | t1 | test.cpp:21:8:21:9 | t1 |
| test.cpp:12:5:12:10 | ... = ... | test.cpp:13:10:13:11 | t2 |
| test.cpp:12:10:12:10 | 0 | test.cpp:12:5:12:10 | ... = ... |
| test.cpp:13:5:13:8 | Phi input | test.cpp:15:3:15:6 | SSA phi read(t2) |
| test.cpp:13:5:13:8 | Phi input | test.cpp:15:3:15:6 | SSA phi(*t2) |
| test.cpp:13:10:13:11 | t2 | test.cpp:13:5:13:8 | Phi input |
| test.cpp:13:10:13:11 | t2 | test.cpp:13:5:13:8 | Phi input |
| test.cpp:15:3:15:6 | SSA phi read(t2) | test.cpp:15:8:15:9 | t2 |
| test.cpp:15:3:15:6 | SSA phi(*t2) | test.cpp:15:8:15:9 | t2 |
| test.cpp:15:8:15:9 | t2 | test.cpp:23:15:23:16 | Phi input |
| test.cpp:15:8:15:9 | t2 | test.cpp:23:15:23:16 | Phi input |
| test.cpp:13:10:13:11 | t2 | test.cpp:15:3:15:6 | Phi |
| test.cpp:13:10:13:11 | t2 | test.cpp:15:3:15:6 | Phi |
| test.cpp:15:3:15:6 | Phi | test.cpp:15:8:15:9 | t2 |
| test.cpp:15:3:15:6 | Phi | test.cpp:15:8:15:9 | t2 |
| test.cpp:15:8:15:9 | t2 | test.cpp:23:19:23:19 | Phi |
| test.cpp:15:8:15:9 | t2 | test.cpp:23:19:23:19 | Phi |
| test.cpp:17:3:17:8 | ... = ... | test.cpp:21:8:21:9 | t1 |
| test.cpp:17:8:17:8 | 0 | test.cpp:17:3:17:8 | ... = ... |
| test.cpp:21:8:21:9 | t1 | test.cpp:23:15:23:16 | Phi input |
| test.cpp:21:8:21:9 | t1 | test.cpp:23:15:23:16 | Phi input |
| test.cpp:21:8:21:9 | t1 | test.cpp:23:19:23:19 | Phi |
| test.cpp:21:8:21:9 | t1 | test.cpp:23:19:23:19 | Phi |
| test.cpp:23:15:23:16 | 0 | test.cpp:23:15:23:16 | 0 |
| test.cpp:23:15:23:16 | 0 | test.cpp:23:15:23:16 | Phi input |
| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi read(*t2) |
| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi read(i) |
| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi read(t1) |
| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi read(t2) |
| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi(*i) |
| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi(*t1) |
| test.cpp:23:19:23:19 | SSA phi read(*t2) | test.cpp:24:10:24:11 | t2 |
| test.cpp:23:19:23:19 | SSA phi read(i) | test.cpp:23:19:23:19 | i |
| test.cpp:23:19:23:19 | SSA phi read(t1) | test.cpp:23:23:23:24 | t1 |
| test.cpp:23:19:23:19 | SSA phi read(t2) | test.cpp:24:10:24:11 | t2 |
| test.cpp:23:19:23:19 | SSA phi(*i) | test.cpp:23:19:23:19 | i |
| test.cpp:23:19:23:19 | SSA phi(*t1) | test.cpp:23:23:23:24 | t1 |
| test.cpp:23:15:23:16 | 0 | test.cpp:23:19:23:19 | Phi |
| test.cpp:23:19:23:19 | Phi | test.cpp:23:19:23:19 | i |
| test.cpp:23:19:23:19 | Phi | test.cpp:23:19:23:19 | i |
| test.cpp:23:19:23:19 | Phi | test.cpp:23:23:23:24 | t1 |
| test.cpp:23:19:23:19 | Phi | test.cpp:23:23:23:24 | t1 |
| test.cpp:23:19:23:19 | Phi | test.cpp:24:10:24:11 | t2 |
| test.cpp:23:19:23:19 | Phi | test.cpp:24:10:24:11 | t2 |
| test.cpp:23:19:23:19 | i | test.cpp:23:27:23:27 | i |
| test.cpp:23:19:23:19 | i | test.cpp:23:27:23:27 | i |
| test.cpp:23:23:23:24 | t1 | test.cpp:23:27:23:29 | Phi input |
| test.cpp:23:23:23:24 | t1 | test.cpp:23:19:23:19 | Phi |
| test.cpp:23:23:23:24 | t1 | test.cpp:26:8:26:9 | t1 |
| test.cpp:23:23:23:24 | t1 | test.cpp:26:8:26:9 | t1 |
| test.cpp:23:27:23:27 | *i | test.cpp:23:27:23:27 | *i |
| test.cpp:23:27:23:27 | *i | test.cpp:23:27:23:27 | i |
| test.cpp:23:27:23:27 | i | test.cpp:23:19:23:19 | Phi |
| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:27 | i |
| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:27 | i |
| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:29 | Phi input |
| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:19:23:19 | Phi |
| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:27:23:29 | ... ++ |
| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:27:23:29 | Phi input |
| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi read(*t2) |
| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi read(i) |
| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi read(t1) |
| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi read(t2) |
| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi(*i) |
| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi(*t1) |
| test.cpp:24:5:24:11 | ... = ... | test.cpp:23:27:23:29 | Phi input |
| test.cpp:24:10:24:11 | t2 | test.cpp:23:27:23:29 | Phi input |
| test.cpp:24:10:24:11 | t2 | test.cpp:23:27:23:29 | Phi input |
| test.cpp:24:5:24:11 | ... = ... | test.cpp:23:19:23:19 | Phi |
| test.cpp:24:10:24:11 | t2 | test.cpp:23:19:23:19 | Phi |
| test.cpp:24:10:24:11 | t2 | test.cpp:23:19:23:19 | Phi |
| test.cpp:24:10:24:11 | t2 | test.cpp:24:5:24:11 | ... = ... |
| test.cpp:382:48:382:54 | source1 | test.cpp:384:16:384:23 | *& ... |
| test.cpp:383:12:383:13 | 0 | test.cpp:383:12:383:13 | 0 |

View File

@@ -12,10 +12,6 @@ astFlow
| BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:62:14:62:14 | x |
| BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:64:14:64:14 | x |
| BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:66:14:66:14 | x |
| BarrierGuard.cpp:81:11:81:16 | call to source | BarrierGuard.cpp:86:8:86:8 | x |
| BarrierGuard.cpp:90:11:90:16 | call to source | BarrierGuard.cpp:101:8:101:8 | x |
| BarrierGuard.cpp:107:11:107:16 | call to source | BarrierGuard.cpp:112:8:112:8 | x |
| BarrierGuard.cpp:116:11:116:16 | call to source | BarrierGuard.cpp:127:8:127:8 | x |
| acrossLinkTargets.cpp:19:27:19:32 | call to source | acrossLinkTargets.cpp:12:8:12:8 | x |
| clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:18:8:18:19 | sourceArray1 |
| clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:22:8:22:20 | & ... |
@@ -157,7 +153,6 @@ irFlow
| clang.cpp:50:25:50:30 | call to source | clang.cpp:53:17:53:26 | *stackArray |
| clang.cpp:50:35:50:40 | call to source | clang.cpp:53:17:53:26 | *stackArray |
| clang.cpp:51:19:51:24 | call to source | clang.cpp:53:17:53:26 | *stackArray |
| clang.cpp:57:21:57:28 | call to source | clang.cpp:59:8:59:8 | d |
| dispatch.cpp:9:37:9:42 | call to source | dispatch.cpp:35:16:35:25 | call to notSource1 |
| dispatch.cpp:9:37:9:42 | call to source | dispatch.cpp:43:15:43:24 | call to notSource1 |
| dispatch.cpp:10:37:10:42 | call to source | dispatch.cpp:36:16:36:25 | call to notSource2 |

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