mirror of
https://github.com/github/codeql.git
synced 2026-06-03 04:40:14 +02:00
Compare commits
1 Commits
tausbn/pyt
...
tausbn/pyt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a87a1a1efa |
4
.bazelrc
4
.bazelrc
@@ -11,8 +11,6 @@ build --compilation_mode opt
|
||||
common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub
|
||||
|
||||
build --repo_env=CC=clang --repo_env=CXX=clang++
|
||||
# Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues)
|
||||
build --repo_env=ANDROID_HOME=
|
||||
|
||||
# print test output, like sembuild does.
|
||||
# Set to `errors` if this is too verbose.
|
||||
@@ -36,7 +34,7 @@ common --@rules_dotnet//dotnet/settings:strict_deps=false
|
||||
common --@rules_rust//rust/toolchain/channel=nightly
|
||||
|
||||
# Reduce this eventually to empty, once we've fixed all our usages of java, and https://github.com/bazel-contrib/rules_go/issues/4193 is fixed
|
||||
common --incompatible_autoload_externally="+@rules_cc,+@rules_java,+@rules_shell"
|
||||
common --incompatible_autoload_externally="+@rules_java,+@rules_shell"
|
||||
|
||||
build --java_language_version=17
|
||||
build --tool_java_language_version=17
|
||||
|
||||
@@ -1 +1 @@
|
||||
9.0.0
|
||||
8.4.2
|
||||
|
||||
1
.github/workflows/ql-for-ql-build.yml
vendored
1
.github/workflows/ql-for-ql-build.yml
vendored
@@ -27,7 +27,6 @@ jobs:
|
||||
uses: github/codeql-action/init@main
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
tools: nightly
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
### Build the extractor ###
|
||||
|
||||
2
.github/workflows/ql-for-ql-tests.yml
vendored
2
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -30,7 +30,6 @@ jobs:
|
||||
uses: github/codeql-action/init@main
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
tools: nightly
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
@@ -76,7 +75,6 @@ jobs:
|
||||
uses: github/codeql-action/init@main
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
tools: nightly
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
|
||||
88
MODULE.bazel
88
MODULE.bazel
@@ -15,23 +15,21 @@ local_path_override(
|
||||
# see https://registry.bazel.build/ for a list of available packages
|
||||
|
||||
bazel_dep(name = "platforms", version = "1.0.0")
|
||||
bazel_dep(name = "rules_cc", version = "0.2.16")
|
||||
bazel_dep(name = "rules_go", version = "0.59.0")
|
||||
bazel_dep(name = "rules_java", version = "9.0.3")
|
||||
bazel_dep(name = "rules_go", version = "0.56.1")
|
||||
bazel_dep(name = "rules_pkg", version = "1.0.1")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.7.3")
|
||||
bazel_dep(name = "rules_python", version = "1.9.0")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.2.0-codeql.1")
|
||||
bazel_dep(name = "rules_python", version = "0.40.0")
|
||||
bazel_dep(name = "rules_shell", version = "0.5.0")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.8.1")
|
||||
bazel_dep(name = "abseil-cpp", version = "20260107.1", repo_name = "absl")
|
||||
bazel_dep(name = "abseil-cpp", version = "20240116.1", repo_name = "absl")
|
||||
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
|
||||
bazel_dep(name = "fmt", version = "12.1.0-codeql.1")
|
||||
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
|
||||
bazel_dep(name = "gazelle", version = "0.47.0")
|
||||
bazel_dep(name = "rules_kotlin", version = "2.1.3-codeql.1")
|
||||
bazel_dep(name = "gazelle", version = "0.40.0")
|
||||
bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1")
|
||||
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
|
||||
bazel_dep(name = "rules_rust", version = "0.68.1.codeql.1")
|
||||
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")
|
||||
bazel_dep(name = "rules_rust", version = "0.66.0")
|
||||
bazel_dep(name = "zstd", version = "1.5.5.bcr.1")
|
||||
|
||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
|
||||
@@ -43,7 +41,7 @@ RUST_EDITION = "2024"
|
||||
# a nightly toolchain is required to enable experimental_use_cc_common_link, which we require internally
|
||||
# we prefer to run the same version as internally, even if experimental_use_cc_common_link is not really
|
||||
# required in this repo
|
||||
RUST_VERSION = "nightly/2026-01-22"
|
||||
RUST_VERSION = "nightly/2025-08-01"
|
||||
|
||||
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
|
||||
rust.toolchain(
|
||||
@@ -55,26 +53,26 @@ rust.toolchain(
|
||||
],
|
||||
# generated by buildutils-internal/scripts/fill-rust-sha256s.py (internal repo)
|
||||
sha256s = {
|
||||
"2026-01-22/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "88db619323cc1321630d124efa51ed02fabc5e020f08cfa0eda2c0ac1afbe69a",
|
||||
"2026-01-22/rustc-nightly-x86_64-apple-darwin.tar.xz": "08484da3fa38db56f93629aeabdc0ae9ff8ed9704c0792d35259cbc849b3f54c",
|
||||
"2026-01-22/rustc-nightly-aarch64-apple-darwin.tar.xz": "a39c0b21b7058e364ea1bd43144e42e4bf1efade036b2e82455f2afce194ee81",
|
||||
"2026-01-22/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "d00248ee9850dbb6932b2578e32ff74fc7c429854c1aa071066ca31b65385a3b",
|
||||
"2026-01-22/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "70656a0ce994ffff16d5a35a7b170a0acd41e9bb54a589c96ed45bf97b094a4d",
|
||||
"2026-01-22/clippy-nightly-x86_64-apple-darwin.tar.xz": "fe242519fa961522734733009705aec3c2d9a20cc57291f2aa614e5e6262c88f",
|
||||
"2026-01-22/clippy-nightly-aarch64-apple-darwin.tar.xz": "38bb226363ec97c9722edf966cd58774a683e19fd2ff2a6030094445d51e06f9",
|
||||
"2026-01-22/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "6da9b4470beea67abfebf046f141eee0d2a8db7c7a9e4e2294478734fd477228",
|
||||
"2026-01-22/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "99004e9d10c43a01499642f53bb3184d41137a95d65bfb217098840a9e79e892",
|
||||
"2026-01-22/cargo-nightly-x86_64-apple-darwin.tar.xz": "6e021394cf8d8400ac6cfdfcef24e4d74f988e91eb8028b36de3a64ce3502990",
|
||||
"2026-01-22/cargo-nightly-aarch64-apple-darwin.tar.xz": "4b2494cb69ab64132cddbc411a38ea9f1105e54d6f986e43168d54f79510c673",
|
||||
"2026-01-22/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "c36613cf57407212d10d37b76e49a60ff42336e953cdff9e177283f530a83fc1",
|
||||
"2026-01-22/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "0b123c5027dbd833aae6845ffe9bd07d309bf798746a7176aadaea68fbcbd05d",
|
||||
"2026-01-22/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "a47864491ad5619158c950ab7570fb6e487d5117338585c27334d45824b406d8",
|
||||
"2026-01-22/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "db9bc826d6e2e7e914505d50157682e516ceb90357e83d77abddc32c2d962f41",
|
||||
"2026-01-22/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "ffaa406932b2fe62e01dad61cf4ed34860a5d2a6f9306ca340d79e630d930039",
|
||||
"2026-01-22/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "e9c0d5e06e18a4b509391b3088f29293e310cdc8ccc865be8fa3f09733326925",
|
||||
"2026-01-22/rust-std-nightly-x86_64-apple-darwin.tar.xz": "25d75995cee679a4828ca9fe48c5a31a67c3b0846018440ef912e5a6208f53f6",
|
||||
"2026-01-22/rust-std-nightly-aarch64-apple-darwin.tar.xz": "e4132bf3f2eed4684c86756a02315bcf481c23e675e3e25630fc604c9cb4594c",
|
||||
"2026-01-22/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "961bb535ef95ae8a5fa4e224cb94aff190f155c45a9bcf7a53e184b024aa41b1",
|
||||
"2025-08-01/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "9bbeaf5d3fc7247d31463a9083aa251c995cc50662c8219e7a2254d76a72a9a4",
|
||||
"2025-08-01/rustc-nightly-x86_64-apple-darwin.tar.xz": "c9ea539a8eff0d5d162701f99f9e1aabe14dd0dfb420d62362817a5d09219de7",
|
||||
"2025-08-01/rustc-nightly-aarch64-apple-darwin.tar.xz": "ae83feebbc39cfd982e4ecc8297731fe79c185173aee138467b334c5404b3773",
|
||||
"2025-08-01/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "9f170c30d802a349be60cf52ec46260802093cb1013ad667fc0d528b7b10152f",
|
||||
"2025-08-01/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "9ae5f3cd8f557c4f6df522597c69d14398cf604cfaed2b83e767c4b77a7eaaf6",
|
||||
"2025-08-01/clippy-nightly-x86_64-apple-darwin.tar.xz": "983cb9ee0b6b968188e04ab2d33743d54764b2681ce565e1b3f2b9135c696a3e",
|
||||
"2025-08-01/clippy-nightly-aarch64-apple-darwin.tar.xz": "ed2219dbc49d088225e1b7c5c4390fa295066e071fddaa2714018f6bb39ddbf0",
|
||||
"2025-08-01/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "911f40ab5cbdd686f40e00965271fe47c4805513a308ed01f30eafb25b448a50",
|
||||
"2025-08-01/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "106463c284e48e4904c717471eeec2be5cc83a9d2cae8d6e948b52438cad2e69",
|
||||
"2025-08-01/cargo-nightly-x86_64-apple-darwin.tar.xz": "6ad35c40efc41a8c531ea43235058347b6902d98a9693bf0aed7fc16d5590cef",
|
||||
"2025-08-01/cargo-nightly-aarch64-apple-darwin.tar.xz": "dd28c365e9d298abc3154c797720ad36a0058f131265c9978b4c8e4e37012c8a",
|
||||
"2025-08-01/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "7b431286e12d6b3834b038f078389a00cac73f351e8c3152b2504a3c06420b3b",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "e342e305d7927cc288d386983b2bc253cfad3776b113386e903d0b302648ef47",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "e44dd3506524d85c37b3a54bcc91d01378fd2c590b2db5c5974d12f05c1b84d1",
|
||||
"2025-08-01/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "0c1b5f46dd81be4a9227b10283a0fcaa39c14fea7e81aea6fd6d9887ff6cdc41",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "423e5fd11406adccbc31b8456ceb7375ce055cdf45e90d2c3babeb2d7f58383f",
|
||||
"2025-08-01/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "3c0ceb46a252647a1d4c7116d9ccae684fa5e42aaf3296419febd2c962c3b41d",
|
||||
"2025-08-01/rust-std-nightly-x86_64-apple-darwin.tar.xz": "3be416003cab10f767390a753d1d16ae4d26c7421c03c98992cf1943e5b0efe8",
|
||||
"2025-08-01/rust-std-nightly-aarch64-apple-darwin.tar.xz": "4046ac0ef951cb056b5028a399124f60999fa37792eab69d008d8d7965f389b4",
|
||||
"2025-08-01/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "191ed9d8603c3a4fe5a7bbbc2feb72049078dae2df3d3b7d5dedf3abbf823e6e",
|
||||
},
|
||||
versions = [RUST_VERSION],
|
||||
)
|
||||
@@ -190,15 +188,6 @@ pip.parse(
|
||||
)
|
||||
use_repo(pip, "codegen_deps")
|
||||
|
||||
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
|
||||
python.toolchain(
|
||||
is_default = True,
|
||||
python_version = "3.12",
|
||||
)
|
||||
use_repo(python, "python_3_12", "python_versions")
|
||||
|
||||
register_toolchains("@python_versions//3.12:all")
|
||||
|
||||
swift_deps = use_extension("//swift/third_party:load.bzl", "swift_deps")
|
||||
|
||||
# following list can be kept in sync with `bazel mod tidy`
|
||||
@@ -232,6 +221,10 @@ use_repo(
|
||||
kotlin_extractor_deps,
|
||||
"codeql_kotlin_defaults",
|
||||
"codeql_kotlin_embeddable",
|
||||
"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",
|
||||
@@ -241,7 +234,10 @@ use_repo(
|
||||
"kotlin-compiler-2.1.20-Beta1",
|
||||
"kotlin-compiler-2.2.0-Beta1",
|
||||
"kotlin-compiler-2.2.20-Beta2",
|
||||
"kotlin-compiler-2.3.0",
|
||||
"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",
|
||||
@@ -251,7 +247,10 @@ use_repo(
|
||||
"kotlin-compiler-embeddable-2.1.20-Beta1",
|
||||
"kotlin-compiler-embeddable-2.2.0-Beta1",
|
||||
"kotlin-compiler-embeddable-2.2.20-Beta2",
|
||||
"kotlin-compiler-embeddable-2.3.0",
|
||||
"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",
|
||||
@@ -261,15 +260,14 @@ use_repo(
|
||||
"kotlin-stdlib-2.1.20-Beta1",
|
||||
"kotlin-stdlib-2.2.0-Beta1",
|
||||
"kotlin-stdlib-2.2.20-Beta2",
|
||||
"kotlin-stdlib-2.3.0",
|
||||
)
|
||||
|
||||
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
|
||||
go_sdk.download(version = "1.26.0")
|
||||
go_sdk.download(version = "1.25.0")
|
||||
|
||||
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
|
||||
go_deps.from_file(go_mod = "//go/extractor:go.mod")
|
||||
use_repo(go_deps, "com_github_stretchr_testify", "org_golang_x_mod", "org_golang_x_tools")
|
||||
use_repo(go_deps, "org_golang_x_mod", "org_golang_x_tools")
|
||||
|
||||
ripunzip_archive = use_repo_rule("//misc/ripunzip:ripunzip.bzl", "ripunzip_archive")
|
||||
|
||||
|
||||
@@ -1,17 +1,3 @@
|
||||
## 0.4.29
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.28
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.27
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a crash when analysing a `${{ ... }}` expression over around 300 characters in length.
|
||||
|
||||
## 0.4.26
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
## 0.4.27
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a crash when analysing a `${{ ... }}` expression over around 300 characters in length.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.4.28
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.4.29
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.29
|
||||
lastReleaseVersion: 0.4.26
|
||||
|
||||
@@ -27,8 +27,8 @@ string getADelimitedExpression(YamlString s, int offset) {
|
||||
// not just the last (greedy match) or first (reluctant match).
|
||||
result =
|
||||
s.getValue()
|
||||
.regexpFind("\\$\\{\\{(?:[^}]|}(?!}))*+\\}\\}", _, offset)
|
||||
.regexpCapture("(\\$\\{\\{(?:[^}]|}(?!}))*+\\}\\})", 1)
|
||||
.regexpFind("\\$\\{\\{(?:[^}]|}(?!}))*\\}\\}", _, offset)
|
||||
.regexpCapture("(\\$\\{\\{(?:[^}]|}(?!}))*\\}\\})", 1)
|
||||
.trim()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-all
|
||||
version: 0.4.30-dev
|
||||
version: 0.4.27-dev
|
||||
library: true
|
||||
warnOnImplicitThis: true
|
||||
dependencies:
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
## 0.6.21
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.20
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.19
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.18
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.6.19
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.6.20
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.6.21
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.21
|
||||
lastReleaseVersion: 0.6.18
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-queries
|
||||
version: 0.6.22-dev
|
||||
version: 0.6.19-dev
|
||||
library: false
|
||||
warnOnImplicitThis: true
|
||||
groups: [actions, queries]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
| 97418 |
|
||||
@@ -1,5 +0,0 @@
|
||||
import codeql.actions.ast.internal.Ast
|
||||
|
||||
int getAnExpressionLength() { result = any(ExpressionImpl e).toString().length() }
|
||||
|
||||
select max(getAnExpressionLength())
|
||||
@@ -1,21 +0,0 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Trap extends @trap {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Tag extends @tag {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from Element e, Trap trap
|
||||
where
|
||||
in_trap_or_tag(e, trap)
|
||||
or
|
||||
exists(Tag tag |
|
||||
in_trap_or_tag(e, tag) and
|
||||
trap_uses_tag(trap, tag)
|
||||
)
|
||||
select e, trap
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
class SourceFile extends @source_file {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Trap extends @trap {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from SourceFile source_file, string name, Trap trap
|
||||
where
|
||||
source_file_uses_trap(source_file, trap) and
|
||||
source_file_name(source_file, name)
|
||||
select name, trap
|
||||
@@ -1,8 +0,0 @@
|
||||
description: Add source_file_name
|
||||
compatibility: backwards
|
||||
source_file_uses_trap.rel: run source_file_uses_trap.ql
|
||||
source_file_name.rel: delete
|
||||
tag_name.rel: delete
|
||||
trap_uses_tag.rel: delete
|
||||
in_trap.rel: run in_trap.ql
|
||||
in_trap_or_tag.rel: delete
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
description: Add trap_filename, source_file_uses_trap and in_trap relations
|
||||
compatibility: full
|
||||
trap_filename.rel: delete
|
||||
source_file_uses_trap.rel: delete
|
||||
in_trap.rel: delete
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
class PreprocessorDirective extends @preprocdirect {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Location extends @location_default {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from PreprocessorDirective ppd, int kind, int kind_new, Location l
|
||||
where
|
||||
preprocdirects(ppd, kind, l) and
|
||||
if kind = 17 then kind_new = /* ppd_warning */ 18 else kind_new = kind
|
||||
select ppd, kind_new, l
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
description: Support embed preprocessor directive
|
||||
compatibility: partial
|
||||
embeds.rel: delete
|
||||
preprocdirects.rel: run preprocdirects.qlo
|
||||
@@ -1,42 +1,3 @@
|
||||
## 8.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
|
||||
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
|
||||
|
||||
## 7.1.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added remote flow source models for the `winhttp.h` windows header and the Azure SDK core library for C/C++.
|
||||
|
||||
## 7.1.0
|
||||
|
||||
### New Features
|
||||
|
||||
* Added a subclass `Embed` of `PreprocessorDirective` for C23 and C++26 `#embed` preprocessor directives.
|
||||
* Added modules `DataFlow::ParameterizedBarrierGuard` and `DataFlow::ParameterizedInstructionBarrierGuard`. These modules provide the same features as `DataFlow::BarrierGuard` and `DataFlow::InstructionBarrierGuard`, but allow for an additional parameter to support properly using them in dataflow configurations that uses flow states.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `Buffer.qll` library will no longer report incorrect buffer sizes on certain malformed databases. As a result, the queries `cpp/static-buffer-overflow`, `cpp/overflow-buffer`, `cpp/badly-bounded-write`, `cpp/overrunning-write`, `cpp/overrunning-write-with-float`, and `cpp/very-likely-overrunning-write` will report fewer false positives on such databases.
|
||||
* Added `taint` summary models and `sql-injection` barrier models for the MySQL `mysql_real_escape_string` and `mysql_real_escape_string_quote` escaping functions.
|
||||
* The predicate `SummarizedCallable.propagatesFlow` has been extended with the columns `Provenance p` and `boolean isExact`, and as a consequence the predicates `SummarizedCallable.hasProvenance` and `SummarizedCallable.hasExactModel` have been removed.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a bug in the `GuardCondition` library which sometimes prevented binary logical operators from being recognized as guard conditions. As a result, queries using `GuardCondition` may see improved results.
|
||||
* Fixed a bug which caused `Node.asDefinition()` to not have a result for certain assignments.
|
||||
|
||||
## 7.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
|
||||
@@ -1,17 +0,0 @@
|
||||
## 7.1.0
|
||||
|
||||
### New Features
|
||||
|
||||
* Added a subclass `Embed` of `PreprocessorDirective` for C23 and C++26 `#embed` preprocessor directives.
|
||||
* Added modules `DataFlow::ParameterizedBarrierGuard` and `DataFlow::ParameterizedInstructionBarrierGuard`. These modules provide the same features as `DataFlow::BarrierGuard` and `DataFlow::InstructionBarrierGuard`, but allow for an additional parameter to support properly using them in dataflow configurations that uses flow states.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `Buffer.qll` library will no longer report incorrect buffer sizes on certain malformed databases. As a result, the queries `cpp/static-buffer-overflow`, `cpp/overflow-buffer`, `cpp/badly-bounded-write`, `cpp/overrunning-write`, `cpp/overrunning-write-with-float`, and `cpp/very-likely-overrunning-write` will report fewer false positives on such databases.
|
||||
* Added `taint` summary models and `sql-injection` barrier models for the MySQL `mysql_real_escape_string` and `mysql_real_escape_string_quote` escaping functions.
|
||||
* The predicate `SummarizedCallable.propagatesFlow` has been extended with the columns `Provenance p` and `boolean isExact`, and as a consequence the predicates `SummarizedCallable.hasProvenance` and `SummarizedCallable.hasExactModel` have been removed.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a bug in the `GuardCondition` library which sometimes prevented binary logical operators from being recognized as guard conditions. As a result, queries using `GuardCondition` may see improved results.
|
||||
* Fixed a bug which caused `Node.asDefinition()` to not have a result for certain assignments.
|
||||
@@ -1,5 +0,0 @@
|
||||
## 7.1.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added remote flow source models for the `winhttp.h` windows header and the Azure SDK core library for C/C++.
|
||||
@@ -1,14 +0,0 @@
|
||||
## 8.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
|
||||
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 8.0.0
|
||||
lastReleaseVersion: 7.0.0
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# partial model of the MySQL api
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["", "", False, "mysql_real_escape_string", "", "", "Argument[*2]", "Argument[*1]", "taint", "manual"]
|
||||
- ["", "", False, "mysql_real_escape_string_quote", "", "", "Argument[*2]", "Argument[*1]", "taint", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: barrierModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, output, kind, provenance
|
||||
- ["", "", False, "mysql_real_escape_string", "", "", "Argument[*1]", "sql-injection", "manual"]
|
||||
- ["", "", False, "mysql_real_escape_string_quote", "", "", "Argument[*1]", "sql-injection", "manual"]
|
||||
@@ -24,13 +24,6 @@ extensions:
|
||||
- ["", "", False, "MapViewOfFileNuma2", "", "", "ReturnValue[*]", "local", "manual"]
|
||||
# ntifs.h
|
||||
- ["", "", False, "NtReadFile", "", "", "Argument[*5]", "local", "manual"]
|
||||
# winhttp.h
|
||||
- ["", "", False, "WinHttpReadData", "", "", "Argument[*1]", "remote", "manual"]
|
||||
- ["", "", False, "WinHttpReadDataEx", "", "", "Argument[*1]", "remote", "manual"]
|
||||
- ["", "", False, "WinHttpQueryHeaders", "", "", "Argument[*3]", "remote", "manual"]
|
||||
- ["", "", False, "WinHttpQueryHeadersEx", "", "", "Argument[*5]", "remote", "manual"]
|
||||
- ["", "", False, "WinHttpQueryHeadersEx", "", "", "Argument[*6]", "remote", "manual"]
|
||||
- ["", "", False, "WinHttpQueryHeadersEx", "", "", "Argument[**8]", "remote", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
@@ -53,6 +46,4 @@ extensions:
|
||||
- ["", "", False, "RtlMoveMemory", "", "", "Argument[*@1]", "Argument[*@0]", "value", "manual"]
|
||||
- ["", "", False, "RtlMoveVolatileMemory", "", "", "Argument[*@1]", "Argument[*@0]", "value", "manual"]
|
||||
# winternl.h
|
||||
- ["", "", False, "RtlInitUnicodeString", "", "", "Argument[*1]", "Argument[*0].Field[*Buffer]", "value", "manual"]
|
||||
# winhttp.h
|
||||
- ["", "", False, "WinHttpCrackUrl", "", "", "Argument[*0]", "Argument[*3]", "taint", "manual"]
|
||||
- ["", "", False, "RtlInitUnicodeString", "", "", "Argument[*1]", "Argument[*0].Field[*Buffer]", "value", "manual"]
|
||||
@@ -1,41 +0,0 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: sourceModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, output, kind, provenance
|
||||
- ["Azure::Core::Http", "RawResponse", True, "GetHeaders", "", "", "ReturnValue[*]", "remote", "manual"]
|
||||
- ["Azure::Core::Http", "RawResponse", True, "GetBody", "", "", "ReturnValue[*]", "remote", "manual"]
|
||||
- ["Azure::Core::Http", "RawResponse", True, "ExtractBodyStream", "", "", "ReturnValue[*]", "remote", "manual"]
|
||||
- ["Azure::Core::Http", "Request", True, "GetHeaders", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["Azure::Core::Http", "Request", True, "GetHeader", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["Azure::Core::Http", "Request", True, "GetBodyStream", "", "", "ReturnValue[*]", "remote", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["Azure::Core", "Url", True, "Url", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "SetScheme", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "SetHost", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "SetPort", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "SetPath", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "SetQueryParameters", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "AppendPath", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "AppendQueryParameter", "", "", "Argument[*1]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "GetHost", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "GetPath", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "GetPort", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "GetQueryParameters", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "GetScheme", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "GetRelativeUrl", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "GetAbsoluteUrl", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "Decode", "", "", "Argument[*0]", "ReturnValue", "taint", "manual"]
|
||||
- ["Azure::Core", "Url", True, "Encode", "", "", "Argument[*0]", "ReturnValue", "taint", "manual"]
|
||||
- ["Azure::Core::IO", "BodyStream", True, "Read", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
|
||||
- ["Azure::Core::IO", "BodyStream", True, "ReadToCount", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
|
||||
- ["Azure::Core::IO", "BodyStream", True, "ReadToEnd", "", "", "Argument[-1]", "ReturnValue.Element", "taint", "manual"]
|
||||
- ["Azure", "Nullable", True, "Nullable", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["Azure", "Nullable", True, "operator=", "", "", "Argument[*0]", "Argument[-1]", "value", "manual"]
|
||||
- ["Azure", "Nullable", True, "Value", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"]
|
||||
- ["Azure", "Nullable", True, "operator->", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"]
|
||||
- ["Azure", "Nullable", True, "operator*", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"]
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 8.0.1-dev
|
||||
version: 7.0.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -192,15 +192,6 @@ class Element extends ElementBase {
|
||||
*/
|
||||
predicate isAffectedByMacro() { affectedByMacro(this) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Holds if this element is affected by the expansion of `mi`.
|
||||
*/
|
||||
predicate isAffectedByMacro(MacroInvocation mi) {
|
||||
affectedbymacroexpansion(underlyingElement(this), unresolveElement(mi))
|
||||
}
|
||||
|
||||
private Element getEnclosingElementPref() {
|
||||
enclosingfunction(underlyingElement(this), unresolveElement(result)) or
|
||||
result.(Function) = stmtEnclosingElement(this) or
|
||||
|
||||
@@ -239,9 +239,6 @@ class MacroInvocation extends MacroAccess {
|
||||
macro_argument_unexpanded(underlyingElement(this), i, result)
|
||||
}
|
||||
|
||||
/** Gets the number of arguments for this macro invocation. */
|
||||
int getNumberOfArguments() { result = count(int i | exists(this.getUnexpandedArgument(i)) | i) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th _expanded_ argument of this macro invocation, where the
|
||||
* first argument has `i = 0`. The result has been expanded for macros _and_
|
||||
|
||||
@@ -328,27 +328,3 @@ class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
|
||||
class PreprocessorLine extends PreprocessorDirective, @ppd_line {
|
||||
override string toString() { result = "#line " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C23 or C++26 `#embed` preprocessor directive. For example, the following code
|
||||
* contains one `Embed` directive:
|
||||
* ```cpp
|
||||
* char arr[] = {
|
||||
* #embed "bin"
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Embed extends PreprocessorDirective, @ppd_embed {
|
||||
override string toString() { result = "#embed " + this.getIncludeText() }
|
||||
|
||||
/**
|
||||
* Gets the token which occurs after `#embed`, for example `"filename"`
|
||||
* or `<filename>`.
|
||||
*/
|
||||
string getIncludeText() { result = this.getHead() }
|
||||
|
||||
/**
|
||||
* Gets the file directly embedded by this `#embed`.
|
||||
*/
|
||||
File getEmbeddedFile() { embeds(underlyingElement(this), unresolveElement(result)) }
|
||||
}
|
||||
|
||||
@@ -62,13 +62,11 @@ private Class getRootType(FieldAccess fa) {
|
||||
* unspecified type of `v` is a `ReferenceType`.
|
||||
*/
|
||||
private int getVariableSize(Variable v) {
|
||||
result =
|
||||
unique(Type t |
|
||||
t = v.getUnspecifiedType() and
|
||||
not t instanceof ReferenceType
|
||||
|
|
||||
t.getSize()
|
||||
)
|
||||
exists(Type t |
|
||||
t = v.getUnspecifiedType() and
|
||||
not t instanceof ReferenceType and
|
||||
result = t.getSize()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,32 +79,30 @@ private int getSize(VariableAccess va) {
|
||||
not v instanceof Field and
|
||||
result = getVariableSize(v)
|
||||
or
|
||||
result =
|
||||
unique(Class c, int trueSize |
|
||||
// Otherwise, we find the "outermost" object and compute the size
|
||||
// as the difference between the size of the type of the "outermost
|
||||
// object" and the offset of the field relative to that type.
|
||||
// For example, consider the following structs:
|
||||
// ```
|
||||
// struct S {
|
||||
// uint32_t x;
|
||||
// uint32_t y;
|
||||
// };
|
||||
// struct S2 {
|
||||
// S s;
|
||||
// uint32_t z;
|
||||
// };
|
||||
// ```
|
||||
// Given an object `S2 s2` the size of the buffer `&s2.s.y`
|
||||
// is the size of the base object type (i.e., `S2`) minus the offset
|
||||
// of `y` relative to the type `S2` (i.e., `4`). So the size of the
|
||||
// buffer is `12 - 4 = 8`.
|
||||
c = getRootType(va) and
|
||||
// we calculate the size based on the last field, to avoid including any padding after it
|
||||
trueSize = max(Field f | | f.getOffsetInClass(c) + getVariableSize(f))
|
||||
|
|
||||
trueSize - v.(Field).getOffsetInClass(c)
|
||||
)
|
||||
exists(Class c, int trueSize |
|
||||
// Otherwise, we find the "outermost" object and compute the size
|
||||
// as the difference between the size of the type of the "outermost
|
||||
// object" and the offset of the field relative to that type.
|
||||
// For example, consider the following structs:
|
||||
// ```
|
||||
// struct S {
|
||||
// uint32_t x;
|
||||
// uint32_t y;
|
||||
// };
|
||||
// struct S2 {
|
||||
// S s;
|
||||
// uint32_t z;
|
||||
// };
|
||||
// ```
|
||||
// Given an object `S2 s2` the size of the buffer `&s2.s.y`
|
||||
// is the size of the base object type (i.e., `S2`) minutes the offset
|
||||
// of `y` relative to the type `S2` (i.e., `4`). So the size of the
|
||||
// buffer is `12 - 4 = 8`.
|
||||
c = getRootType(va) and
|
||||
// we calculate the size based on the last field, to avoid including any padding after it
|
||||
trueSize = max(Field f | | f.getOffsetInClass(c) + getVariableSize(f)) and
|
||||
result = trueSize - v.(Field).getOffsetInClass(c)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -120,8 +116,12 @@ private int isSource(Expr bufferExpr, Element why) {
|
||||
exists(Variable bufferVar | bufferVar = bufferExpr.(VariableAccess).getTarget() |
|
||||
// buffer is a fixed size array
|
||||
exists(bufferVar.getUnspecifiedType().(ArrayType).getSize()) and
|
||||
// more generous than .getSize() itself, when the array is a class field or similar.
|
||||
result = getSize(bufferExpr) and
|
||||
result =
|
||||
unique(int size | // more generous than .getSize() itself, when the array is a class field or similar.
|
||||
size = getSize(bufferExpr)
|
||||
|
|
||||
size
|
||||
) and
|
||||
why = bufferVar and
|
||||
not memberMayBeVarSize(_, bufferVar) and
|
||||
not exists(BuiltInOperationBuiltInOffsetOf offsetof | offsetof.getAChild*() = bufferExpr) and
|
||||
|
||||
@@ -14,9 +14,7 @@ class PackedTimeType extends Type {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate timeType(string typeName) {
|
||||
typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm", "TIME_FIELDS", "_TIME_FIELDS", "PTIME_FIELDS"]
|
||||
}
|
||||
private predicate timeType(string typeName) { typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm"] }
|
||||
|
||||
/**
|
||||
* A type that is used to represent times and dates in an 'unpacked' form, that is,
|
||||
@@ -97,24 +95,3 @@ class StructTmMonthFieldAccess extends MonthFieldAccess {
|
||||
class StructTmYearFieldAccess extends YearFieldAccess {
|
||||
StructTmYearFieldAccess() { this.getTarget().getName() = "tm_year" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `DayFieldAccess` for the `TIME_FIELDS` struct.
|
||||
*/
|
||||
class TimeFieldsDayFieldAccess extends DayFieldAccess {
|
||||
TimeFieldsDayFieldAccess() { this.getTarget().getName() = "Day" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `MonthFieldAccess` for the `TIME_FIELDS` struct.
|
||||
*/
|
||||
class TimeFieldsMonthFieldAccess extends MonthFieldAccess {
|
||||
TimeFieldsMonthFieldAccess() { this.getTarget().getName() = "Month" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `YearFieldAccess` for the `TIME_FIELDS` struct.
|
||||
*/
|
||||
class TimeFieldsYearFieldAccess extends YearFieldAccess {
|
||||
TimeFieldsYearFieldAccess() { this.getTarget().getName() = "Year" }
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Void
|
||||
private import codeql.controlflow.Guards as SharedGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr as TE
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedFunction as TF
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
|
||||
|
||||
private class BasicBlock = IRCfg::BasicBlock;
|
||||
@@ -684,26 +683,24 @@ final class GuardCondition = GuardConditionImpl;
|
||||
*/
|
||||
private class GuardConditionFromBinaryLogicalOperator extends GuardConditionImpl instanceof Cpp::BinaryLogicalOperation
|
||||
{
|
||||
GuardConditionImpl l;
|
||||
GuardConditionImpl r;
|
||||
|
||||
GuardConditionFromBinaryLogicalOperator() {
|
||||
super.getLeftOperand() = l and
|
||||
super.getRightOperand() = r
|
||||
}
|
||||
|
||||
override predicate valueControls(Cpp::BasicBlock controlled, GuardValue v) {
|
||||
// `l || r` does not control `r` even though `l` does.
|
||||
not r.(Cpp::Expr).getBasicBlock() = controlled and
|
||||
l.valueControls(controlled, v)
|
||||
or
|
||||
r.valueControls(controlled, v)
|
||||
exists(Cpp::BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
|
||||
this = binop and
|
||||
lhs = binop.getLeftOperand() and
|
||||
rhs = binop.getRightOperand() and
|
||||
lhs.valueControls(controlled, v) and
|
||||
rhs.valueControls(controlled, v)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate valueControlsEdge(Cpp::BasicBlock pred, Cpp::BasicBlock succ, GuardValue v) {
|
||||
l.valueControlsEdge(pred, succ, v)
|
||||
or
|
||||
r.valueControlsEdge(pred, succ, v)
|
||||
exists(Cpp::BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
|
||||
this = binop and
|
||||
lhs = binop.getLeftOperand() and
|
||||
rhs = binop.getRightOperand() and
|
||||
lhs.valueControlsEdge(pred, succ, v) and
|
||||
rhs.valueControlsEdge(pred, succ, v)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1029,7 +1026,7 @@ private class GuardConditionFromIR extends GuardConditionImpl {
|
||||
|
||||
private predicate excludeAsControlledInstruction(Instruction instr) {
|
||||
// Exclude the temporaries generated by a ternary expression.
|
||||
exists(TE::TranslatedConditionalExpr tce |
|
||||
exists(TranslatedConditionalExpr tce |
|
||||
instr = tce.getInstruction(ConditionValueFalseStoreTag())
|
||||
or
|
||||
instr = tce.getInstruction(ConditionValueTrueStoreTag())
|
||||
@@ -1041,14 +1038,6 @@ private predicate excludeAsControlledInstruction(Instruction instr) {
|
||||
or
|
||||
// Exclude unreached instructions, as their AST is the whole function and not a block.
|
||||
instr instanceof UnreachedInstruction
|
||||
or
|
||||
// Exclude instructions generated by a translated function as they map to the function itself
|
||||
// and the function is considered the last basic block of a function body.
|
||||
any(TF::TranslatedFunction tf).getInstruction(_) = instr
|
||||
or
|
||||
// `ChiInstruction`s generated by instructions in the above case don't come from `getInstruction` (since they are generated by AliasedSSA)
|
||||
// so we need to special case them.
|
||||
excludeAsControlledInstruction(instr.(ChiInstruction).getPartial())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
|
||||
import cpp
|
||||
private import new.DataFlow
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as Private
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import internal.FlowSummaryImpl
|
||||
@@ -368,8 +367,6 @@ private predicate elementSpec(
|
||||
) {
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
|
||||
barrierModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
|
||||
barrierGuardModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _)
|
||||
}
|
||||
|
||||
@@ -555,7 +552,6 @@ private Locatable getSupportedFunctionTemplateArgument(Function templateFunction
|
||||
* Normalize the `n`'th parameter of `f` by replacing template names
|
||||
* with `func:N` (where `N` is the index of the template).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
|
||||
exists(Function templateFunction |
|
||||
templateFunction = getFullyTemplatedFunction(f) and
|
||||
@@ -1032,84 +1028,6 @@ private module Cached {
|
||||
isSinkNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TKindModelPair =
|
||||
TMkPair(string kind, string model) { isBarrierGuardNode(_, _, kind, model) }
|
||||
|
||||
private GuardValue convertAcceptingValue(Public::AcceptingValue av) {
|
||||
av.isTrue() and result.asBooleanValue() = true
|
||||
or
|
||||
av.isFalse() and result.asBooleanValue() = false
|
||||
or
|
||||
// NOTE: The below cases don't contribute anything currently since the
|
||||
// callers immediately use `.asBooleanValue()` to convert the `GuardValue`
|
||||
// to a boolean. Once we're willing to accept the breaking change of
|
||||
// converting the barrier guard API to use `GuardValue`s instead `Boolean`s
|
||||
// we can remove this restriction.
|
||||
av.isNoException() and result.getDualValue().isThrowsException()
|
||||
or
|
||||
av.isZero() and result.asIntValue() = 0
|
||||
or
|
||||
av.isNotZero() and result.getDualValue().asIntValue() = 0
|
||||
or
|
||||
av.isNull() and result.isNullValue()
|
||||
or
|
||||
av.isNotNull() and result.isNonNullValue()
|
||||
}
|
||||
|
||||
private predicate barrierGuardChecks(IRGuardCondition g, Expr e, boolean gv, TKindModelPair kmp) {
|
||||
exists(
|
||||
SourceSinkInterpretationInput::InterpretNode n, Public::AcceptingValue acceptingvalue,
|
||||
string kind, string model
|
||||
|
|
||||
isBarrierGuardNode(n, acceptingvalue, kind, model) and
|
||||
n.asNode().asExpr() = e and
|
||||
kmp = TMkPair(kind, model) and
|
||||
gv = convertAcceptingValue(acceptingvalue).asBooleanValue() and
|
||||
n.asNode().(Private::ArgumentNode).getCall().asCallInstruction() = g
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TKindModelPairIntPair =
|
||||
MkKindModelPairIntPair(TKindModelPair pair, int indirectionIndex) {
|
||||
indirectionIndex > 0 and
|
||||
Private::nodeHasInstruction(_, _, indirectionIndex) and
|
||||
exists(pair)
|
||||
}
|
||||
|
||||
private predicate indirectBarrierGuardChecks(
|
||||
IRGuardCondition g, Expr e, boolean gv, TKindModelPairIntPair kmp
|
||||
) {
|
||||
exists(
|
||||
SourceSinkInterpretationInput::InterpretNode interpretNode,
|
||||
Public::AcceptingValue acceptingvalue, string kind, string model, int indirectionIndex,
|
||||
Private::ArgumentNode arg
|
||||
|
|
||||
isBarrierGuardNode(interpretNode, acceptingvalue, kind, model) and
|
||||
arg = interpretNode.asNode() and
|
||||
arg.asIndirectExpr(indirectionIndex) = e and
|
||||
kmp = MkKindModelPairIntPair(TMkPair(kind, model), indirectionIndex) and
|
||||
gv = convertAcceptingValue(acceptingvalue).asBooleanValue() and
|
||||
arg.getCall().asCallInstruction() = g
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate barrierNode(DataFlow::Node node, string kind, string model) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isBarrierNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
or
|
||||
DataFlow::ParameterizedBarrierGuard<TKindModelPair, barrierGuardChecks/4>::getABarrierNode(TMkPair(kind,
|
||||
model)) = node
|
||||
or
|
||||
DataFlow::ParameterizedBarrierGuard<TKindModelPairIntPair, indirectBarrierGuardChecks/4>::getAnIndirectBarrierNode(MkKindModelPairIntPair(TMkPair(kind,
|
||||
model), _)) = node
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -1126,12 +1044,6 @@ predicate sourceNode(DataFlow::Node node, string kind) { sourceNode(node, kind,
|
||||
*/
|
||||
predicate sinkNode(DataFlow::Node node, string kind) { sinkNode(node, kind, _) }
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
predicate barrierNode(DataFlow::Node node, string kind) { barrierNode(node, kind, _) }
|
||||
|
||||
private predicate interpretSummary(
|
||||
Function f, string input, string output, string kind, string provenance, string model
|
||||
) {
|
||||
@@ -1146,22 +1058,40 @@ private predicate interpretSummary(
|
||||
|
||||
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||
private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
string input_;
|
||||
string output_;
|
||||
string kind;
|
||||
Provenance p_;
|
||||
string model_;
|
||||
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _, _) }
|
||||
|
||||
SummarizedCallableAdapter() { interpretSummary(this, input_, output_, kind, p_, model_) }
|
||||
private predicate relevantSummaryElementManual(
|
||||
string input, string output, string kind, string model
|
||||
) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance, model) and
|
||||
provenance.isManual()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElementGenerated(
|
||||
string input, string output, string kind, string model
|
||||
) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance, model) and
|
||||
provenance.isGenerated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(
|
||||
string input, string output, boolean preservesValue, Provenance p, boolean isExact, string model
|
||||
string input, string output, boolean preservesValue, string model
|
||||
) {
|
||||
input = input_ and
|
||||
output = output_ and
|
||||
(if kind = "value" then preservesValue = true else preservesValue = false) and
|
||||
p = p_ and
|
||||
isExact = true and
|
||||
model = model_
|
||||
exists(string kind |
|
||||
this.relevantSummaryElementManual(input, output, kind, model)
|
||||
or
|
||||
not this.relevantSummaryElementManual(_, _, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind, model)
|
||||
|
|
||||
if kind = "value" then preservesValue = true else preservesValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) {
|
||||
interpretSummary(this, _, _, _, provenance, _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
predicate callableFromSource(SummarizedCallableBase c) { exists(c.getBlock()) }
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result = TDirectPosition(-1) }
|
||||
|
||||
ReturnKind getStandardReturnValueKind() { result = getReturnValueKind("") }
|
||||
@@ -151,27 +149,16 @@ module SourceSinkInterpretationInput implements
|
||||
}
|
||||
|
||||
predicate barrierElement(
|
||||
Element e, string output, string kind, Public::Provenance provenance, string model
|
||||
Element n, string output, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
barrierModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance, model) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
none()
|
||||
}
|
||||
|
||||
predicate barrierGuardElement(
|
||||
Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
|
||||
Element n, string input, Public::AcceptingValue acceptingvalue, string kind,
|
||||
Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingvalue, kind,
|
||||
provenance, model) and
|
||||
e = interpretElement(package, type, subtypes, name, signature, ext)
|
||||
)
|
||||
none()
|
||||
}
|
||||
|
||||
private newtype TInterpretNode =
|
||||
@@ -201,7 +188,7 @@ module SourceSinkInterpretationInput implements
|
||||
string toString() {
|
||||
result = this.asElement().toString()
|
||||
or
|
||||
result = this.asNode().toStringImpl()
|
||||
result = this.asNode().toString()
|
||||
or
|
||||
result = this.asCall().toString()
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Expr extends StmtParent, @expr {
|
||||
predicate hasChild(Expr e, int n) { e = this.getChild(n) }
|
||||
|
||||
/** Gets the enclosing function of this expression, if any. */
|
||||
override Function getEnclosingFunction() { result = exprEnclosingElement(this) }
|
||||
Function getEnclosingFunction() { result = exprEnclosingElement(this) }
|
||||
|
||||
/** Gets the nearest enclosing set of curly braces around this expression in the source, if any. */
|
||||
BlockStmt getEnclosingBlock() { result = this.getEnclosingStmt().getEnclosingBlock() }
|
||||
|
||||
@@ -34,38 +34,6 @@ private string getSingleLocationFilePath(@element e) {
|
||||
macroinvocations(e, _, loc, _)
|
||||
or
|
||||
preprocdirects(e, _, loc)
|
||||
or
|
||||
diagnostics(e, _, _, _, _, loc)
|
||||
or
|
||||
usings(e, _, loc, _)
|
||||
or
|
||||
static_asserts(e, _, _, loc, _)
|
||||
or
|
||||
derivations(e, _, _, _, loc)
|
||||
or
|
||||
frienddecls(e, _, _, loc)
|
||||
or
|
||||
comments(e, _, loc)
|
||||
or
|
||||
exprs(e, _, loc)
|
||||
or
|
||||
stmts(e, _, loc)
|
||||
or
|
||||
initialisers(e, _, _, loc)
|
||||
or
|
||||
attributes(e, _, _, _, loc)
|
||||
or
|
||||
attribute_args(e, _, _, _, loc)
|
||||
or
|
||||
namequalifiers(e, _, _, loc)
|
||||
or
|
||||
enumconstants(e, _, _, _, _, loc)
|
||||
or
|
||||
type_mentions(e, _, loc, _)
|
||||
or
|
||||
lambda_capture(e, _, _, _, _, _, loc)
|
||||
or
|
||||
concept_templates(e, _, loc)
|
||||
|
|
||||
result = getLocationFilePath(loc)
|
||||
)
|
||||
@@ -77,13 +45,13 @@ private string getSingleLocationFilePath(@element e) {
|
||||
overlay[local]
|
||||
private string getMultiLocationFilePath(@element e) {
|
||||
exists(@location_default loc |
|
||||
var_decls(_, e, _, _, loc)
|
||||
exists(@var_decl vd | var_decls(vd, e, _, _, loc))
|
||||
or
|
||||
fun_decls(_, e, _, _, loc)
|
||||
exists(@fun_decl fd | fun_decls(fd, e, _, _, loc))
|
||||
or
|
||||
type_decls(_, e, loc)
|
||||
exists(@type_decl td | type_decls(td, e, loc))
|
||||
or
|
||||
namespace_decls(_, e, loc, _)
|
||||
exists(@namespace_decl nd | namespace_decls(nd, e, loc, _))
|
||||
|
|
||||
result = getLocationFilePath(loc)
|
||||
)
|
||||
@@ -94,29 +62,19 @@ private string getMultiLocationFilePath(@element e) {
|
||||
* overlay variant.
|
||||
*/
|
||||
overlay[local]
|
||||
private predicate isBase() { not isOverlay() }
|
||||
|
||||
/**
|
||||
* Holds if `path` was extracted in the overlay database.
|
||||
*/
|
||||
overlay[local]
|
||||
private predicate overlayHasFile(string path) {
|
||||
isOverlay() and
|
||||
files(_, path) and
|
||||
path != ""
|
||||
}
|
||||
private predicate holdsInBase() { not isOverlay() }
|
||||
|
||||
/**
|
||||
* Discards an element from the base variant if:
|
||||
* - It has a single location in a file extracted in the overlay, or
|
||||
* - All of its locations are in files extracted in the overlay.
|
||||
* - It has a single location in a changed file, or
|
||||
* - All of its locations are in changed files.
|
||||
*/
|
||||
overlay[discard_entity]
|
||||
private predicate discardElement(@element e) {
|
||||
isBase() and
|
||||
holdsInBase() and
|
||||
(
|
||||
overlayHasFile(getSingleLocationFilePath(e))
|
||||
overlayChangedFiles(getSingleLocationFilePath(e))
|
||||
or
|
||||
forex(string path | path = getMultiLocationFilePath(e) | overlayHasFile(path))
|
||||
forex(string path | path = getMultiLocationFilePath(e) | overlayChangedFiles(path))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,145 +8,83 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Provides an inter-procedural must-flow data flow analysis.
|
||||
* A configuration of a data flow analysis that performs must-flow analysis. This is different
|
||||
* from `DataFlow.qll` which performs may-flow analysis (i.e., it finds paths where the source _may_
|
||||
* flow to the sink).
|
||||
*
|
||||
* Like in `DataFlow.qll`, each use of the `MustFlow.qll` library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string and override `isSource`, `isSink` (and
|
||||
* `isAdditionalFlowStep` if additional steps are required).
|
||||
*/
|
||||
module MustFlow {
|
||||
/**
|
||||
* An input configuration of a data flow analysis that performs must-flow analysis. This is different
|
||||
* from `DataFlow.qll` which performs may-flow analysis (i.e., it finds paths where the source _may_
|
||||
* flow to the sink).
|
||||
*/
|
||||
signature module ConfigSig {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Instruction source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Operand sink);
|
||||
|
||||
/**
|
||||
* Holds if data flow through `instr` is prohibited.
|
||||
*/
|
||||
default predicate isBarrier(Instruction instr) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the additional flow step from `node1` to `node2` must be taken
|
||||
* into account in the analysis.
|
||||
*/
|
||||
default predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }
|
||||
|
||||
/** Holds if this configuration allows flow from arguments to parameters. */
|
||||
default predicate allowInterproceduralFlow() { any() }
|
||||
}
|
||||
abstract class MustFlowConfiguration extends string {
|
||||
bindingset[this]
|
||||
MustFlowConfiguration() { any() }
|
||||
|
||||
/**
|
||||
* Constructs a global must-flow computation.
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
module Global<ConfigSig Config> {
|
||||
import Config
|
||||
abstract predicate isSource(Instruction source);
|
||||
|
||||
/**
|
||||
* Holds if data must flow from `source` to `sink`.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate flowPath(PathNode source, PathSink sink) {
|
||||
isSource(source.getInstruction()) and
|
||||
source.getASuccessor*() = sink
|
||||
}
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
abstract predicate isSink(Operand sink);
|
||||
|
||||
/** Holds if `node` flows from a source. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsFromSource(Instruction node) {
|
||||
not isBarrier(node) and
|
||||
(
|
||||
isSource(node)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(mid, node) and
|
||||
flowsFromSource(mid)
|
||||
)
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Holds if data flow through `instr` is prohibited.
|
||||
*/
|
||||
predicate isBarrier(Instruction instr) { none() }
|
||||
|
||||
/** Holds if `node` flows to a sink. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsToSink(Instruction node) {
|
||||
flowsFromSource(node) and
|
||||
(
|
||||
isSink(node.getAUse())
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(node, mid) and
|
||||
flowsToSink(mid)
|
||||
)
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Holds if the additional flow step from `node1` to `node2` must be taken
|
||||
* into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }
|
||||
|
||||
/** Holds if `nodeFrom` flows to `nodeTo`. */
|
||||
private predicate step(Instruction nodeFrom, Instruction nodeTo) {
|
||||
Cached::localStep(nodeFrom, nodeTo)
|
||||
or
|
||||
allowInterproceduralFlow() and
|
||||
Cached::flowThroughCallable(nodeFrom, nodeTo)
|
||||
or
|
||||
isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
|
||||
}
|
||||
/** Holds if this configuration allows flow from arguments to parameters. */
|
||||
predicate allowInterproceduralFlow() { any() }
|
||||
|
||||
private newtype TLocalPathNode =
|
||||
MkLocalPathNode(Instruction n) {
|
||||
flowsToSink(n) and
|
||||
(
|
||||
isSource(n)
|
||||
or
|
||||
exists(PathNode mid | step(mid.getInstruction(), n))
|
||||
)
|
||||
}
|
||||
|
||||
/** A `Node` that is in a path from a source to a sink. */
|
||||
class PathNode extends TLocalPathNode {
|
||||
Instruction n;
|
||||
|
||||
PathNode() { this = MkLocalPathNode(n) }
|
||||
|
||||
/** Gets the underlying node. */
|
||||
Instruction getInstruction() { result = n }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() { result = n.getAst().toString() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = n.getLocation() }
|
||||
|
||||
/** Gets a successor node, if any. */
|
||||
PathNode getASuccessor() { step(this.getInstruction(), result.getInstruction()) }
|
||||
}
|
||||
|
||||
private class PathSink extends PathNode {
|
||||
PathSink() { isSink(this.getInstruction().getAUse()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph {
|
||||
private predicate reach(PathNode n) { n instanceof PathSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Holds if data must flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
|
||||
this.isSource(source.getInstruction()) and
|
||||
source.getASuccessor*() = sink
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `node` flows from a source. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
|
||||
not config.isBarrier(node) and
|
||||
(
|
||||
config.isSource(node)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(mid, node, config) and
|
||||
flowsFromSource(mid, pragma[only_bind_into](config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` flows to a sink. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsToSink(Instruction node, MustFlowConfiguration config) {
|
||||
flowsFromSource(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
config.isSink(node.getAUse())
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(node, mid, config) and
|
||||
flowsToSink(mid, pragma[only_bind_into](config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */
|
||||
@@ -164,7 +102,7 @@ private module Cached {
|
||||
not f.isVirtual() and
|
||||
call.getPositionalArgument(n) = instr and
|
||||
f = call.getStaticCallTarget() and
|
||||
isEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n))
|
||||
}
|
||||
|
||||
@@ -173,7 +111,7 @@ private module Cached {
|
||||
* corresponding initialization instruction that receives the value of `instr` in `f`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate isPositionalArgumentInitParam(
|
||||
private predicate getPositionalArgumentInitParam(
|
||||
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
exists(int n |
|
||||
@@ -188,18 +126,18 @@ private module Cached {
|
||||
* `instr` in `f`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate isThisArgumentInitParam(
|
||||
private predicate getThisArgumentInitParam(
|
||||
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
call.getStaticCallTarget() = f and
|
||||
isEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
call.getThisArgument() = instr and
|
||||
init.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
|
||||
/** Holds if `f` is the enclosing non-virtual function of `init`. */
|
||||
private predicate isEnclosingNonVirtualFunctionInitializeParameter(
|
||||
private predicate getEnclosingNonVirtualFunctionInitializeParameter(
|
||||
InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
@@ -207,7 +145,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
/** Holds if `f` is the enclosing non-virtual function of `init`. */
|
||||
private predicate isEnclosingNonVirtualFunctionInitializeIndirection(
|
||||
private predicate getEnclosingNonVirtualFunctionInitializeIndirection(
|
||||
InitializeIndirectionInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
@@ -215,16 +153,15 @@ private module Cached {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `argument` is an argument (or argument indirection) to a call, and
|
||||
* `parameter` is the corresponding initialization instruction in the call target.
|
||||
* Holds if `instr` is an argument (or argument indirection) to a call, and
|
||||
* `succ` is the corresponding initialization instruction in the call target.
|
||||
*/
|
||||
cached
|
||||
predicate flowThroughCallable(Instruction argument, Instruction parameter) {
|
||||
private predicate flowThroughCallable(Instruction argument, Instruction parameter) {
|
||||
// Flow from an argument to a parameter
|
||||
exists(CallInstruction call, InitializeParameterInstruction init | init = parameter |
|
||||
isPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget())
|
||||
getPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget())
|
||||
or
|
||||
isThisArgumentInitParam(call, argument, init, call.getStaticCallTarget())
|
||||
getThisArgumentInitParam(call, argument, init, call.getStaticCallTarget())
|
||||
)
|
||||
or
|
||||
// Flow from argument indirection to parameter indirection
|
||||
@@ -233,7 +170,7 @@ private module Cached {
|
||||
|
|
||||
init = parameter and
|
||||
read.getPrimaryInstruction() = call and
|
||||
isEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
||||
getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
||||
|
|
||||
exists(int n |
|
||||
read.getSideEffectOperand().getAnyDef() = argument and
|
||||
@@ -268,10 +205,92 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate localStep(Instruction nodeFrom, Instruction nodeTo) {
|
||||
predicate step(Instruction nodeFrom, Instruction nodeTo) {
|
||||
exists(Operand mid |
|
||||
instructionToOperandStep(nodeFrom, mid) and
|
||||
operandToInstructionStep(mid, nodeTo)
|
||||
)
|
||||
or
|
||||
flowThroughCallable(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this
|
||||
* predicate ensures that joins go from `n` to the result instead of the other
|
||||
* way around.
|
||||
*/
|
||||
pragma[inline]
|
||||
private IRFunction getEnclosingCallable(Instruction n) {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/** Holds if `nodeFrom` flows to `nodeTo`. */
|
||||
private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) {
|
||||
exists(config) and
|
||||
Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
|
||||
(
|
||||
config.allowInterproceduralFlow()
|
||||
or
|
||||
getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
|
||||
)
|
||||
or
|
||||
config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
|
||||
}
|
||||
|
||||
private newtype TLocalPathNode =
|
||||
MkLocalPathNode(Instruction n, MustFlowConfiguration config) {
|
||||
flowsToSink(n, config) and
|
||||
(
|
||||
config.isSource(n)
|
||||
or
|
||||
exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config))
|
||||
)
|
||||
}
|
||||
|
||||
/** A `Node` that is in a path from a source to a sink. */
|
||||
class MustFlowPathNode extends TLocalPathNode {
|
||||
Instruction n;
|
||||
|
||||
MustFlowPathNode() { this = MkLocalPathNode(n, _) }
|
||||
|
||||
/** Gets the underlying node. */
|
||||
Instruction getInstruction() { result = n }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() { result = n.getAst().toString() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = n.getLocation() }
|
||||
|
||||
/** Gets a successor node, if any. */
|
||||
MustFlowPathNode getASuccessor() {
|
||||
step(this.getInstruction(), result.getInstruction(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
MustFlowConfiguration getConfiguration() { this = MkLocalPathNode(_, result) }
|
||||
}
|
||||
|
||||
private class MustFlowPathSink extends MustFlowPathNode {
|
||||
MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph {
|
||||
private predicate reach(MustFlowPathNode n) {
|
||||
n instanceof MustFlowPathSink or reach(n.getASuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(MustFlowPathNode a, MustFlowPathNode b) {
|
||||
a.getASuccessor() = b and reach(b)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(MustFlowPathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
private import cpp as Cpp
|
||||
private import DataFlowUtil
|
||||
private import DataFlowNodes
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
@@ -17,42 +16,28 @@ private import semmle.code.cpp.dataflow.ExternalFlow as External
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
newtype TIRDataFlowNode0 =
|
||||
TInstructionNode0(Instruction i) {
|
||||
not Ssa::ignoreInstruction(i) and
|
||||
not exists(Operand op |
|
||||
not Ssa::ignoreOperand(op) and i = Ssa::getIRRepresentationOfOperand(op)
|
||||
) and
|
||||
// We exclude `void`-typed instructions because they cannot contain data.
|
||||
// However, if the instruction is a glvalue, and their type is `void`, then the result
|
||||
// type of the instruction is really `void*`, and thus we still want to have a dataflow
|
||||
// node for it.
|
||||
(not i.getResultType() instanceof VoidType or i.isGLValue())
|
||||
} or
|
||||
TMultipleUseOperandNode0(Operand op) {
|
||||
not Ssa::ignoreOperand(op) and not exists(Ssa::getIRRepresentationOfOperand(op))
|
||||
} or
|
||||
TSingleUseOperandNode0(Operand op) {
|
||||
not Ssa::ignoreOperand(op) and exists(Ssa::getIRRepresentationOfOperand(op))
|
||||
}
|
||||
|
||||
cached
|
||||
string toStringCached(Node n) {
|
||||
result = toExprString(n)
|
||||
or
|
||||
not exists(toExprString(n)) and
|
||||
result = n.toStringImpl()
|
||||
module Nodes0 {
|
||||
cached
|
||||
newtype TIRDataFlowNode0 =
|
||||
TInstructionNode0(Instruction i) {
|
||||
not Ssa::ignoreInstruction(i) and
|
||||
not exists(Operand op |
|
||||
not Ssa::ignoreOperand(op) and i = Ssa::getIRRepresentationOfOperand(op)
|
||||
) and
|
||||
// We exclude `void`-typed instructions because they cannot contain data.
|
||||
// However, if the instruction is a glvalue, and their type is `void`, then the result
|
||||
// type of the instruction is really `void*`, and thus we still want to have a dataflow
|
||||
// node for it.
|
||||
(not i.getResultType() instanceof VoidType or i.isGLValue())
|
||||
} or
|
||||
TMultipleUseOperandNode0(Operand op) {
|
||||
not Ssa::ignoreOperand(op) and not exists(Ssa::getIRRepresentationOfOperand(op))
|
||||
} or
|
||||
TSingleUseOperandNode0(Operand op) {
|
||||
not Ssa::ignoreOperand(op) and exists(Ssa::getIRRepresentationOfOperand(op))
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
Location getLocationCached(Node n) { result = n.getLocationImpl() }
|
||||
|
||||
cached
|
||||
newtype TContentApprox =
|
||||
TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or
|
||||
TUnionApproxContent(string s) { unionHasApproxName(_, s) } or
|
||||
TElementApproxContent()
|
||||
|
||||
/**
|
||||
* Gets an additional term that is added to the `join` and `branch` computations to reflect
|
||||
* an additional forward or backwards branching factor that is not taken into account
|
||||
@@ -74,174 +59,38 @@ private module Cached {
|
||||
result = countNumberOfBranchesUsingParameter(switch, p)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TDataFlowCallable =
|
||||
TSourceCallable(Cpp::Declaration decl) or
|
||||
TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c)
|
||||
|
||||
cached
|
||||
newtype TDataFlowCall =
|
||||
TNormalCall(CallInstruction call) or
|
||||
TSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
) {
|
||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
cached
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
exists(GlobalLikeVariable v |
|
||||
exists(Ssa::GlobalUse globalUse |
|
||||
v = globalUse.getVariable() and
|
||||
n1.(FinalGlobalValue).getGlobalUse() = globalUse
|
||||
|
|
||||
globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and
|
||||
v = n2.asVariable()
|
||||
or
|
||||
v = n2.asIndirectVariable(globalUse.getIndirection())
|
||||
)
|
||||
or
|
||||
exists(Ssa::GlobalDef globalDef |
|
||||
v = globalDef.getVariable() and
|
||||
n2.(InitialGlobalValue).getGlobalDef() = globalDef
|
||||
|
|
||||
globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and
|
||||
v = n1.asVariable()
|
||||
or
|
||||
v = n1.asIndirectVariable(globalDef.getIndirection())
|
||||
)
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(),
|
||||
n2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*
|
||||
* The boolean `certain` is true if the destination address does not involve
|
||||
* any pointer arithmetic, and false otherwise.
|
||||
*/
|
||||
cached
|
||||
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
|
||||
exists(
|
||||
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
|
||||
StoreInstruction store, FieldContent fc
|
||||
|
|
||||
postFieldUpdate = node2 and
|
||||
fc = c and
|
||||
nodeHasInstruction(node1, pragma[only_bind_into](store),
|
||||
pragma[only_bind_into](indirectionIndex1)) and
|
||||
postFieldUpdate.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain) and
|
||||
fc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode()) and
|
||||
certain = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
cached
|
||||
predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
cached
|
||||
predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
exists(
|
||||
FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc
|
||||
|
|
||||
fc = c and
|
||||
nodeHasOperand(node2, operand, indirectionIndex2) and
|
||||
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
|
||||
// in `storeStep`.
|
||||
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
|
||||
fc.getAField() = fa1.getField() and
|
||||
getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`.
|
||||
*/
|
||||
cached
|
||||
predicate clearsContent(Node n, ContentSet c) {
|
||||
n =
|
||||
any(PostUpdateNode pun, Content d |
|
||||
d.impliesClearOf(c) and storeStepImpl(_, d, pun, true)
|
||||
|
|
||||
pun
|
||||
).getPreUpdateNode() and
|
||||
(
|
||||
not exists(Operand op, Cpp::Operation p |
|
||||
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
|
||||
(
|
||||
p instanceof Cpp::AssignPointerAddExpr or
|
||||
p instanceof Cpp::AssignPointerSubExpr or
|
||||
p instanceof Cpp::CrementOperation
|
||||
)
|
||||
|
|
||||
p.getAnOperand() = op.getUse().getAst()
|
||||
)
|
||||
or
|
||||
forex(PostUpdateNode pun, Content d |
|
||||
pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and
|
||||
storeStepImpl(_, d, pun, true) and
|
||||
pun.getPreUpdateNode() = n
|
||||
|
|
||||
c.(Content).getIndirectionIndex() = d.getIndirectionIndex()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
private int getNumberOfIndirections(Node n) {
|
||||
result = n.(RawIndirectOperand).getIndirectionIndex()
|
||||
or
|
||||
result = n.(RawIndirectInstruction).getIndirectionIndex()
|
||||
or
|
||||
result = n.(VariableNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
|
||||
or
|
||||
result = n.(FinalParameterNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(BodyLessParameterNodeImpl).getIndirectionIndex()
|
||||
}
|
||||
private import Nodes0
|
||||
|
||||
/**
|
||||
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
|
||||
* output for `n`.
|
||||
* A module for calculating the number of stars (i.e., `*`s) needed for various
|
||||
* dataflow node `toString` predicates.
|
||||
*/
|
||||
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
|
||||
module NodeStars {
|
||||
private int getNumberOfIndirections(Node n) {
|
||||
result = n.(RawIndirectOperand).getIndirectionIndex()
|
||||
or
|
||||
result = n.(RawIndirectInstruction).getIndirectionIndex()
|
||||
or
|
||||
result = n.(VariableNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
|
||||
or
|
||||
result = n.(FinalParameterNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(BodyLessParameterNodeImpl).getIndirectionIndex()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
|
||||
* output for `n`.
|
||||
*/
|
||||
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
|
||||
}
|
||||
|
||||
import NodeStars
|
||||
|
||||
/**
|
||||
* A cut-down `DataFlow::Node` class that does not depend on the output of SSA.
|
||||
@@ -979,10 +828,85 @@ private int getMinIndirectionForGlobalDef(Ssa::GlobalDef def) {
|
||||
result = getMinIndirectionsForType(def.getUnspecifiedType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
exists(GlobalLikeVariable v |
|
||||
exists(Ssa::GlobalUse globalUse |
|
||||
v = globalUse.getVariable() and
|
||||
n1.(FinalGlobalValue).getGlobalUse() = globalUse
|
||||
|
|
||||
globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and
|
||||
v = n2.asVariable()
|
||||
or
|
||||
v = n2.asIndirectVariable(globalUse.getIndirection())
|
||||
)
|
||||
or
|
||||
exists(Ssa::GlobalDef globalDef |
|
||||
v = globalDef.getVariable() and
|
||||
n2.(InitialGlobalValue).getGlobalDef() = globalDef
|
||||
|
|
||||
globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and
|
||||
v = n1.asVariable()
|
||||
or
|
||||
v = n1.asIndirectVariable(globalDef.getIndirection())
|
||||
)
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(),
|
||||
n2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
bindingset[c]
|
||||
pragma[inline_late]
|
||||
private int getIndirectionIndexLate(Content c) { result = c.getIndirectionIndex() }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*
|
||||
* The boolean `certain` is true if the destination address does not involve
|
||||
* any pointer arithmetic, and false otherwise. This has to do with whether a
|
||||
* store step can be used to clear a field (see `clearsContent`).
|
||||
*/
|
||||
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
|
||||
exists(
|
||||
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
|
||||
StoreInstruction store, FieldContent fc
|
||||
|
|
||||
postFieldUpdate = node2 and
|
||||
fc = c and
|
||||
nodeHasInstruction(node1, pragma[only_bind_into](store),
|
||||
pragma[only_bind_into](indirectionIndex1)) and
|
||||
postFieldUpdate.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain) and
|
||||
fc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode()) and
|
||||
certain = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) }
|
||||
|
||||
/**
|
||||
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
|
||||
* operations and exactly `n` `LoadInstruction` operations.
|
||||
*/
|
||||
private predicate numberOfLoadsFromOperandRec(
|
||||
Operand operandFrom, Operand operandTo, int ind, boolean certain
|
||||
) {
|
||||
@@ -1033,6 +957,63 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex)
|
||||
hasInstructionAndIndex(node, instr, indirectionIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
exists(
|
||||
FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc
|
||||
|
|
||||
fc = c and
|
||||
nodeHasOperand(node2, operand, indirectionIndex2) and
|
||||
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
|
||||
// in `storeStep`.
|
||||
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
|
||||
fc.getAField() = fa1.getField() and
|
||||
getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`.
|
||||
*/
|
||||
predicate clearsContent(Node n, ContentSet c) {
|
||||
n =
|
||||
any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun)
|
||||
.getPreUpdateNode() and
|
||||
(
|
||||
// The crement operations and pointer addition and subtraction self-assign. We do not
|
||||
// want to clear the contents if it is indirectly pointed at by any of these operations,
|
||||
// as part of the contents might still be accessible afterwards. If there is no such
|
||||
// indirection clearing the contents is safe.
|
||||
not exists(Operand op, Cpp::Operation p |
|
||||
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
|
||||
(
|
||||
p instanceof Cpp::AssignPointerAddExpr or
|
||||
p instanceof Cpp::AssignPointerSubExpr or
|
||||
p instanceof Cpp::CrementOperation
|
||||
)
|
||||
|
|
||||
p.getAnOperand() = op.getUse().getAst()
|
||||
)
|
||||
or
|
||||
forex(PostUpdateNode pun, Content d |
|
||||
pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and
|
||||
storeStepImpl(_, d, pun, true) and
|
||||
pun.getPreUpdateNode() = n
|
||||
|
|
||||
c.(Content).getIndirectionIndex() = d.getIndirectionIndex()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
@@ -1065,6 +1046,11 @@ class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDataFlowCallable =
|
||||
TSourceCallable(Cpp::Declaration decl) or
|
||||
TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c)
|
||||
|
||||
/**
|
||||
* A callable, which may be:
|
||||
* - a function (that may contain code)
|
||||
@@ -1148,8 +1134,17 @@ class DataFlowType extends TypeFinal {
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDataFlowCall =
|
||||
TNormalCall(CallInstruction call) or
|
||||
TSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
) {
|
||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||
}
|
||||
|
||||
private predicate summarizedCallableIsManual(SummarizedCallable sc) {
|
||||
sc.asSummarizedCallable().hasManualModel()
|
||||
sc.asSummarizedCallable().applyManualModel()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1528,6 +1523,12 @@ private predicate fieldHasApproxName(Field f, string s) {
|
||||
|
||||
private predicate unionHasApproxName(Cpp::Union u, string s) { s = u.getName().charAt(0) }
|
||||
|
||||
cached
|
||||
private newtype TContentApprox =
|
||||
TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or
|
||||
TUnionApproxContent(string s) { unionHasApproxName(_, s) } or
|
||||
TElementApproxContent()
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox extends TContentApprox {
|
||||
string toString() { none() } // overridden in subclasses
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowNodes
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
@@ -73,9 +73,17 @@ private module Cached {
|
||||
// a result for `getConvertedResultExpression`. We remap this here so that
|
||||
// this `ConvertInstruction` maps to the result of the expression that
|
||||
// represents the extent.
|
||||
result = IRConstruction::Raw::getAllocationExtentConvertExpr(instr)
|
||||
exists(TranslatedNonConstantAllocationSize tas |
|
||||
result = tas.getExtent().getExpr() and
|
||||
instr = tas.getInstruction(AllocationExtentConvertTag())
|
||||
)
|
||||
or
|
||||
result = IRConstruction::Raw::getTransparentConversionParenthesisExpr(instr)
|
||||
// There's no instruction that returns `ParenthesisExpr`, but some queries
|
||||
// expect this
|
||||
exists(TranslatedTransparentConversion ttc |
|
||||
result = ttc.getExpr().(ParenthesisExpr) and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
or
|
||||
// Certain expressions generate `CopyValueInstruction`s only when they
|
||||
// are needed. Examples of this include crement operations and compound
|
||||
@@ -104,10 +112,10 @@ private module Cached {
|
||||
// needed, and in that case the only value that will propagate forward in
|
||||
// the program is the value that's been updated. So in those cases we just
|
||||
// use the result of `node.asDefinition()` as the result of `node.asExpr()`.
|
||||
exists(StoreInstruction store |
|
||||
store = instr and
|
||||
IRConstruction::Raw::instructionProducesExprResult(store) and
|
||||
result = asDefinitionImpl0(store)
|
||||
exists(TranslatedCoreExpr tco |
|
||||
tco.getInstruction(_) = instr and
|
||||
tco.producesExprResult() and
|
||||
result = asDefinitionImpl0(instr)
|
||||
)
|
||||
or
|
||||
// IR construction breaks an array aggregate literal `{1, 2, 3}` into a
|
||||
@@ -137,9 +145,18 @@ private module Cached {
|
||||
// For an expression such as `i += 2` we pretend that the generated
|
||||
// `StoreInstruction` contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
result = IRConstruction::Raw::getAssignOperationStoreExpr(store)
|
||||
exists(TranslatedAssignOperation tao |
|
||||
store = tao.getInstruction(AssignmentStoreTag()) and
|
||||
result = tao.getExpr()
|
||||
)
|
||||
or
|
||||
result = IRConstruction::Raw::getCrementOperationStoreExpr(store)
|
||||
// Similarly for `i++` and `++i` we pretend that the generated
|
||||
// `StoreInstruction` contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedCrementOperation tco |
|
||||
store = tco.getInstruction(CrementStoreTag()) and
|
||||
result = tco.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +166,11 @@ private module Cached {
|
||||
*/
|
||||
private predicate excludeAsDefinitionResult(StoreInstruction store) {
|
||||
// Exclude the store to the temporary generated by a ternary expression.
|
||||
IRConstruction::Raw::isConditionalExprTempStore(store)
|
||||
exists(TranslatedConditionalExpr tce |
|
||||
store = tce.getInstruction(ConditionValueFalseStoreTag())
|
||||
or
|
||||
store = tce.getInstruction(ConditionValueTrueStoreTag())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||
private import DataFlowUtil
|
||||
private import DataFlowNodes
|
||||
private import DataFlowPrivate
|
||||
private import SsaImpl as Ssa
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
|
||||
private import PrintIRUtilities
|
||||
|
||||
/** A property provider for local IR dataflow store steps. */
|
||||
|
||||
@@ -2,7 +2,6 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
|
||||
private import SsaImpl as Ssa
|
||||
private import PrintIRUtilities
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
|
||||
|
||||
private Instruction getInstruction(Node n, string stars) {
|
||||
result = [n.asInstruction(), n.(RawIndirectInstruction).getInstruction()] and
|
||||
|
||||
@@ -10,85 +10,22 @@ private import semmle.code.cpp.models.interfaces.PartialFlow as PartialFlow
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as FIO
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedInitialization
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowNodes
|
||||
import SsaImplCommon
|
||||
|
||||
private module SourceVariables {
|
||||
/**
|
||||
* Holds if `store` is the `StoreInstruction` generated by a postfix
|
||||
* increment or decrement operation `e`, and `postCrement` is the operand
|
||||
* that represents the use of the evaluated value of `e`.
|
||||
*/
|
||||
private predicate isUseAfterPostfixCrement0(StoreInstruction store, Operand postCrement) {
|
||||
exists(
|
||||
BinaryInstruction binary, IRBlock b, int iPre, int iPost, int iStore, Operand preCrement,
|
||||
Instruction left
|
||||
|
|
||||
binary instanceof AddInstruction
|
||||
or
|
||||
binary instanceof PointerAddInstruction
|
||||
or
|
||||
binary instanceof SubInstruction
|
||||
or
|
||||
binary instanceof PointerSubInstruction
|
||||
|
|
||||
store.getSourceValue() = binary and
|
||||
left = binary.getLeft() and
|
||||
strictcount(left.getAUse()) = 2 and
|
||||
left.getAUse() = preCrement and
|
||||
left.getAUse() = postCrement and
|
||||
b.getInstruction(iPre) = preCrement.getUse() and
|
||||
b.getInstruction(iPost) = postCrement.getUse() and
|
||||
b.getInstruction(iStore) = store and
|
||||
iPre < iStore and
|
||||
iStore < iPost
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `store` is the `StoreInstruction` generated by an postfix
|
||||
* increment or decrement operation `e`, and `postCrement` is the fully
|
||||
* converted operand that represents the use of the evaluated value of `e`.
|
||||
*/
|
||||
private predicate isUseAfterPostfixCrement(StoreInstruction store, Operand postCrement) {
|
||||
isUseAfterPostfixCrement0(store, postCrement) and
|
||||
conversionFlow(postCrement, _, false, _)
|
||||
or
|
||||
exists(Instruction instr, Operand postCrement0 |
|
||||
isUseAfterPostfixCrement(store, postCrement0) and
|
||||
conversionFlow(postCrement0, instr, false, _) and
|
||||
instr = postCrement.getDef()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSavedPostfixCrementSourceVariable(
|
||||
BaseSourceVariable base, StoreInstruction store, int ind
|
||||
) {
|
||||
exists(BaseSourceVariableInstruction inst, int ind0 |
|
||||
isUseAfterPostfixCrement(store, _) and
|
||||
inst.getBaseSourceVariable() = base and
|
||||
isDef(_, _, store.getDestinationAddressOperand(), inst, ind0, 0) and
|
||||
ind = [ind0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TSourceVariable =
|
||||
TNormalSourceVariable(BaseSourceVariable base, int ind) {
|
||||
TMkSourceVariable(BaseSourceVariable base, int ind) {
|
||||
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||
} or
|
||||
TSavedPostfixCrementSourceVariable(StoreInstruction store, int ind) {
|
||||
hasSavedPostfixCrementSourceVariable(_, store, ind)
|
||||
}
|
||||
|
||||
abstract private class AbstractSourceVariable extends TSourceVariable {
|
||||
class SourceVariable extends TSourceVariable {
|
||||
BaseSourceVariable base;
|
||||
int ind;
|
||||
|
||||
bindingset[ind]
|
||||
AbstractSourceVariable() { any() }
|
||||
SourceVariable() { this = TMkSourceVariable(base, ind) }
|
||||
|
||||
/** Gets the IR variable associated with this `SourceVariable`, if any. */
|
||||
IRVariable getIRVariable() { result = base.(BaseIRVariable).getIRVariable() }
|
||||
@@ -100,7 +37,7 @@ private module SourceVariables {
|
||||
BaseSourceVariable getBaseVariable() { result = base }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
string toString() { result = repeatStars(this.getIndirection()) + base.toString() }
|
||||
|
||||
/**
|
||||
* Gets the number of loads performed on the base source variable
|
||||
@@ -125,53 +62,6 @@ private module SourceVariables {
|
||||
/** Gets the location of this variable. */
|
||||
Location getLocation() { result = this.getBaseVariable().getLocation() }
|
||||
}
|
||||
|
||||
final class SourceVariable = AbstractSourceVariable;
|
||||
|
||||
/**
|
||||
* A regular source variable. Most source variables are instances of this
|
||||
* class.
|
||||
*/
|
||||
class NormalSourceVariable extends AbstractSourceVariable, TNormalSourceVariable {
|
||||
NormalSourceVariable() { this = TNormalSourceVariable(base, ind) }
|
||||
|
||||
final override string toString() {
|
||||
result = repeatStars(this.getIndirection()) + base.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Before a value is postfix incremented (or decremented) we "save" its
|
||||
* current value so that the pre-incremented value can be returned to the
|
||||
* enclosing expression. We use the source variables represented by this
|
||||
* class to represent the "saved value".
|
||||
*/
|
||||
class SavedPostfixCrementSourceVariable extends AbstractSourceVariable,
|
||||
TSavedPostfixCrementSourceVariable
|
||||
{
|
||||
StoreInstruction store;
|
||||
|
||||
SavedPostfixCrementSourceVariable() {
|
||||
this = TSavedPostfixCrementSourceVariable(store, ind) and
|
||||
hasSavedPostfixCrementSourceVariable(base, store, ind)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
result = repeatStars(this.getIndirection()) + base.toString() + " [before crement]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `StoreInstruction` that writes the incremented (or decremented)
|
||||
* value.
|
||||
*/
|
||||
StoreInstruction getStoreInstruction() { result = store }
|
||||
|
||||
/**
|
||||
* Gets the fully converted `Operand` that represents the use of the
|
||||
* value before the increment.
|
||||
*/
|
||||
Operand getOperand() { isUseAfterPostfixCrement(store, result) }
|
||||
}
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
@@ -219,43 +109,17 @@ private newtype TDefImpl =
|
||||
TDirectDefImpl(Operand address, int indirectionIndex) {
|
||||
isDef(_, _, address, _, _, indirectionIndex)
|
||||
} or
|
||||
TSavedPostfixCrementDefImpl(SavedPostfixCrementSourceVariable sv, int indirectionIndex) {
|
||||
isDef(_, _, sv.getStoreInstruction().getDestinationAddressOperand(), _, sv.getIndirection(),
|
||||
indirectionIndex)
|
||||
} or
|
||||
TGlobalDefImpl(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
|
||||
// Represents the initial "definition" of a global variable when entering
|
||||
// a function body.
|
||||
isGlobalDefImpl(v, f, _, indirectionIndex)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasOperandAndIndirection(
|
||||
SavedPostfixCrementSourceVariable sv, Operand operand, int indirection
|
||||
) {
|
||||
sv.getOperand() = operand and
|
||||
sv.getIndirection() = indirection
|
||||
}
|
||||
|
||||
private predicate hasBeforePostCrementUseImpl(
|
||||
SavedPostfixCrementSourceVariable sv, Operand operand, int indirectionIndex
|
||||
) {
|
||||
not isDef(true, _, operand, _, _, _) and
|
||||
exists(int indirection |
|
||||
hasOperandAndIndirection(sv, operand, indirection) and
|
||||
isUse(_, operand, _, indirection, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TUseImpl =
|
||||
TDirectUseImpl(Operand operand, int indirectionIndex) {
|
||||
isUse(_, operand, _, _, indirectionIndex) and
|
||||
not isDef(true, _, operand, _, _, _) and
|
||||
not hasBeforePostCrementUseImpl(_, operand, indirectionIndex)
|
||||
} or
|
||||
TSavedPostfixCrementUseImpl(SavedPostfixCrementSourceVariable sv, int indirectionIndex) {
|
||||
hasBeforePostCrementUseImpl(sv, _, indirectionIndex)
|
||||
not isDef(true, _, operand, _, _, _)
|
||||
} or
|
||||
TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
|
||||
// Represents a final "use" of a global variable to ensure that
|
||||
@@ -359,8 +223,19 @@ abstract class DefImpl extends TDefImpl {
|
||||
*/
|
||||
abstract int getIndirection();
|
||||
|
||||
/**
|
||||
* Gets the base source variable (i.e., the variable without
|
||||
* any indirection) of this definition or use.
|
||||
*/
|
||||
abstract BaseSourceVariable getBaseSourceVariable();
|
||||
|
||||
/** Gets the variable that is defined or used. */
|
||||
abstract SourceVariable getSourceVariable();
|
||||
SourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v, int indirection |
|
||||
sourceVariableHasBaseAndIndex(result, v, indirection) and
|
||||
defHasSourceVariable(this, v, indirection)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this definition is guaranteed to totally overwrite the
|
||||
@@ -368,8 +243,8 @@ abstract class DefImpl extends TDefImpl {
|
||||
*/
|
||||
abstract predicate isCertain();
|
||||
|
||||
/** Gets the value written to the destination variable by this definition, if any. */
|
||||
Node0Impl getValue() { none() }
|
||||
/** Gets the value written to the destination variable by this definition. */
|
||||
abstract Node0Impl getValue();
|
||||
|
||||
/** Gets the operand that represents the address of this definition, if any. */
|
||||
Operand getAddressOperand() { none() }
|
||||
@@ -418,8 +293,19 @@ abstract class UseImpl extends TUseImpl {
|
||||
/** Gets the indirection index of this use. */
|
||||
final int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
/**
|
||||
* Gets the base source variable (i.e., the variable without
|
||||
* any indirection) of this definition or use.
|
||||
*/
|
||||
abstract BaseSourceVariable getBaseSourceVariable();
|
||||
|
||||
/** Gets the variable that is defined or used. */
|
||||
abstract SourceVariable getSourceVariable();
|
||||
SourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v, int indirection |
|
||||
sourceVariableHasBaseAndIndex(result, v, indirection) and
|
||||
useHasSourceVariable(this, v, indirection)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this use is guaranteed to read the
|
||||
@@ -428,6 +314,18 @@ abstract class UseImpl extends TUseImpl {
|
||||
abstract predicate isCertain();
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defHasSourceVariable(DefImpl def, BaseSourceVariable bv, int ind) {
|
||||
bv = def.getBaseSourceVariable() and
|
||||
ind = def.getIndirection()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate useHasSourceVariable(UseImpl use, BaseSourceVariable bv, int ind) {
|
||||
bv = use.getBaseSourceVariable() and
|
||||
ind = use.getIndirection()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVariable bv, int ind) {
|
||||
v.getBaseVariable() = bv and
|
||||
@@ -439,7 +337,10 @@ private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVari
|
||||
* initialize `v`.
|
||||
*/
|
||||
private Instruction getInitializationTargetAddress(IRVariable v) {
|
||||
result = IRConstruction::Raw::getInitializationTargetAddress(v)
|
||||
exists(TranslatedVariableInitialization init |
|
||||
init.getIRVariable() = v and
|
||||
result = init.getTargetAddress()
|
||||
)
|
||||
}
|
||||
|
||||
/** An initial definition of an SSA variable address. */
|
||||
@@ -457,12 +358,16 @@ abstract private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||
|
||||
final override predicate isCertain() { any() }
|
||||
|
||||
final override Node0Impl getValue() { none() }
|
||||
|
||||
override Cpp::Location getLocation() { result = v.getLocation() }
|
||||
|
||||
final override NormalSourceVariable getSourceVariable() {
|
||||
final override SourceVariable getSourceVariable() {
|
||||
result.getBaseVariable() = v and
|
||||
result.getIndirection() = 0
|
||||
}
|
||||
|
||||
final override BaseSourceVariable getBaseSourceVariable() { result = v }
|
||||
}
|
||||
|
||||
private class DefVariableAddressImpl extends DefAddressImpl {
|
||||
@@ -508,17 +413,8 @@ private class DirectDef extends DefImpl, TDirectDefImpl {
|
||||
isDef(_, _, address, result, _, indirectionIndex)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasBaseSourceVariableAndIndirection(BaseSourceVariable v, int indirection) {
|
||||
v = this.getBase().getBaseSourceVariable() and
|
||||
indirection = this.getIndirection()
|
||||
}
|
||||
|
||||
final override NormalSourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v, int indirection |
|
||||
sourceVariableHasBaseAndIndex(result, v, indirection) and
|
||||
this.hasBaseSourceVariableAndIndirection(v, indirection)
|
||||
)
|
||||
override BaseSourceVariable getBaseSourceVariable() {
|
||||
result = this.getBase().getBaseSourceVariable()
|
||||
}
|
||||
|
||||
override int getIndirection() { isDef(_, _, address, _, result, indirectionIndex) }
|
||||
@@ -528,32 +424,6 @@ private class DirectDef extends DefImpl, TDirectDefImpl {
|
||||
override predicate isCertain() { isDef(true, _, address, _, _, indirectionIndex) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A definition that "saves" the value of a variable before it is incremented
|
||||
* or decremented.
|
||||
*/
|
||||
private class SavedPostfixCrementDefImpl extends DefImpl, TSavedPostfixCrementDefImpl {
|
||||
SavedPostfixCrementSourceVariable sv;
|
||||
|
||||
SavedPostfixCrementDefImpl() { this = TSavedPostfixCrementDefImpl(sv, indirectionIndex) }
|
||||
|
||||
override Cpp::Location getLocation() { result = sv.getStoreInstruction().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
sv.getStoreInstruction() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
override string toString() { result = "Def of " + this.getSourceVariable() }
|
||||
|
||||
override SourceVariable getSourceVariable() { result = sv }
|
||||
|
||||
override int getIndirection() { result = sv.getIndirection() }
|
||||
|
||||
override predicate isCertain() {
|
||||
isDef(true, _, sv.getStoreInstruction().getDestinationAddressOperand(), _, _, indirectionIndex)
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectUseImpl extends UseImpl, TDirectUseImpl {
|
||||
Operand operand;
|
||||
|
||||
@@ -562,22 +432,29 @@ private class DirectUseImpl extends UseImpl, TDirectUseImpl {
|
||||
override string toString() { result = "Use of " + this.getSourceVariable() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
operand.getUse() = block.getInstruction(index)
|
||||
// See the comment in `ssa0`'s `OperandBasedUse` for an explanation of this
|
||||
// predicate's implementation.
|
||||
if this.getBase().getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
|
||||
then
|
||||
exists(Operand op, int indirection, Instruction base |
|
||||
indirection = this.getIndirection() and
|
||||
base = this.getBase() and
|
||||
op =
|
||||
min(Operand cand, int i |
|
||||
isUse(_, cand, base, indirection, indirectionIndex) and
|
||||
block.getInstruction(i) = cand.getUse()
|
||||
|
|
||||
cand order by i
|
||||
) and
|
||||
block.getInstruction(index) = op.getUse()
|
||||
)
|
||||
else operand.getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
private BaseSourceVariableInstruction getBase() { isUse(_, operand, result, _, indirectionIndex) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasBaseSourceVariableAndIndirection(BaseSourceVariable bv, int indirection) {
|
||||
this.getBase().getBaseSourceVariable() = bv and
|
||||
this.getIndirection() = indirection
|
||||
}
|
||||
|
||||
override NormalSourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v, int indirection |
|
||||
sourceVariableHasBaseAndIndex(result, v, indirection) and
|
||||
this.hasBaseSourceVariableAndIndirection(v, indirection)
|
||||
)
|
||||
override BaseSourceVariable getBaseSourceVariable() {
|
||||
result = this.getBase().getBaseSourceVariable()
|
||||
}
|
||||
|
||||
final Operand getOperand() { result = operand }
|
||||
@@ -591,34 +468,6 @@ private class DirectUseImpl extends UseImpl, TDirectUseImpl {
|
||||
override Node getNode() { nodeHasOperand(result, operand, indirectionIndex) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The use of the original "saved" variable after the variable has been incremented
|
||||
* or decremented.
|
||||
*/
|
||||
private class SavedPostfixCrementUseImpl extends UseImpl, TSavedPostfixCrementUseImpl {
|
||||
SavedPostfixCrementSourceVariable sv;
|
||||
|
||||
SavedPostfixCrementUseImpl() { this = TSavedPostfixCrementUseImpl(sv, indirectionIndex) }
|
||||
|
||||
override string toString() { result = "Use of " + this.getSourceVariable() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getOperand().getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
override SourceVariable getSourceVariable() { result = sv }
|
||||
|
||||
final Operand getOperand() { result = sv.getOperand() }
|
||||
|
||||
final override Cpp::Location getLocation() { result = this.getOperand().getLocation() }
|
||||
|
||||
override int getIndirection() { result = sv.getIndirection() }
|
||||
|
||||
override predicate isCertain() { isUse(true, this.getOperand(), _, _, indirectionIndex) }
|
||||
|
||||
override Node getNode() { nodeHasOperand(result, this.getOperand(), indirectionIndex) }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate finalParameterNodeHasParameterAndIndex(
|
||||
FinalParameterNode n, Parameter p, int indirectionIndex
|
||||
@@ -683,18 +532,7 @@ class FinalParameterUse extends UseImpl, TFinalParameterUse {
|
||||
result instanceof UnknownLocation
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasBaseSourceVariableAndIndirection(BaseIRVariable v, int indirection) {
|
||||
v.getIRVariable().getAst() = p and
|
||||
indirection = this.getIndirection()
|
||||
}
|
||||
|
||||
override NormalSourceVariable getSourceVariable() {
|
||||
exists(BaseIRVariable v, int indirection |
|
||||
sourceVariableHasBaseAndIndex(result, v, indirection) and
|
||||
this.hasBaseSourceVariableAndIndirection(v, indirection)
|
||||
)
|
||||
}
|
||||
override BaseIRVariable getBaseSourceVariable() { result.getIRVariable().getAst() = p }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -774,17 +612,8 @@ class GlobalUse extends UseImpl, TGlobalUse {
|
||||
hasReturnPosition(f, block, index)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasBaseSourceVariableAndIndirection(BaseIRVariable v, int indirection) {
|
||||
baseSourceVariableIsGlobal(v, global, f) and
|
||||
indirection = this.getIndirection()
|
||||
}
|
||||
|
||||
override NormalSourceVariable getSourceVariable() {
|
||||
exists(BaseIRVariable v, int indirection |
|
||||
sourceVariableHasBaseAndIndex(result, v, indirection) and
|
||||
this.hasBaseSourceVariableAndIndirection(v, indirection)
|
||||
)
|
||||
override BaseSourceVariable getBaseSourceVariable() {
|
||||
baseSourceVariableIsGlobal(result, global, f)
|
||||
}
|
||||
|
||||
final override Cpp::Location getLocation() { result = f.getLocation() }
|
||||
@@ -829,15 +658,15 @@ class GlobalDefImpl extends DefImpl, TGlobalDefImpl {
|
||||
)
|
||||
}
|
||||
|
||||
final override NormalSourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v |
|
||||
sourceVariableHasBaseAndIndex(result, v, indirectionIndex) and
|
||||
baseSourceVariableIsGlobal(v, global, f)
|
||||
)
|
||||
/** Gets the global variable associated with this definition. */
|
||||
override BaseSourceVariable getBaseSourceVariable() {
|
||||
baseSourceVariableIsGlobal(result, global, f)
|
||||
}
|
||||
|
||||
override int getIndirection() { result = indirectionIndex }
|
||||
|
||||
override Node0Impl getValue() { none() }
|
||||
|
||||
override predicate isCertain() { any() }
|
||||
|
||||
/**
|
||||
@@ -875,15 +704,9 @@ predicate defToNode(Node node, Definition def, SourceVariable sv) {
|
||||
}
|
||||
|
||||
private predicate defToNode(Node node, Definition def) {
|
||||
// Only definitions of `NormalSourceVariable` need to be converted into
|
||||
// dataflow nodes. The other case, `SavedPostfixCrementSourceVariable`,
|
||||
// are internal definitions that don't have a dataflow node representation.
|
||||
def.getSourceVariable() instanceof NormalSourceVariable and
|
||||
(
|
||||
nodeHasOperand(node, def.getValue().asOperand(), def.getIndirectionIndex())
|
||||
or
|
||||
nodeHasInstruction(node, def.getValue().asInstruction(), def.getIndirectionIndex())
|
||||
)
|
||||
nodeHasOperand(node, def.getValue().asOperand(), def.getIndirectionIndex())
|
||||
or
|
||||
nodeHasInstruction(node, def.getValue().asInstruction(), def.getIndirectionIndex())
|
||||
or
|
||||
node.(InitialGlobalValue).getGlobalDef() = def
|
||||
}
|
||||
@@ -1117,16 +940,6 @@ module SsaCached {
|
||||
SsaImpl::phiHasInputFromBlock(phi, inp, bb)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate uncertainWriteDefinitionInput(Definition uncertain, Definition inp) {
|
||||
SsaImpl::uncertainWriteDefinitionInput(uncertain, inp)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaDefReachesEndOfBlock(IRBlock bb, Definition def) {
|
||||
SsaImpl::ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
predicate variableRead = SsaInput::variableRead/4;
|
||||
|
||||
predicate variableWrite = SsaInput::variableWrite/4;
|
||||
@@ -1222,23 +1035,13 @@ class SynthNode extends DataFlowIntegrationImpl::SsaNode {
|
||||
SynthNode() { not this.asDefinition() instanceof SsaImpl::WriteDefinition }
|
||||
}
|
||||
|
||||
private signature class ParamSig;
|
||||
signature predicate guardChecksNodeSig(IRGuards::IRGuardCondition g, Node e, boolean branch);
|
||||
|
||||
private module ParamIntPair<ParamSig P> {
|
||||
newtype TPair = MkPair(P p, int indirectionIndex) { nodeHasInstruction(_, _, indirectionIndex) }
|
||||
}
|
||||
signature predicate guardChecksNodeSig(
|
||||
IRGuards::IRGuardCondition g, Node e, boolean branch, int indirectionIndex
|
||||
);
|
||||
|
||||
private module WithParam<ParamSig P> {
|
||||
signature predicate guardChecksNodeSig(IRGuards::IRGuardCondition g, Node e, boolean gv, P param);
|
||||
}
|
||||
|
||||
private module IntWithParam<ParamSig P> {
|
||||
signature predicate guardChecksNodeSig(
|
||||
IRGuards::IRGuardCondition g, Node e, boolean gv, int indirectionIndex, P param
|
||||
);
|
||||
}
|
||||
|
||||
module BarrierGuardWithIntParam<ParamSig P, IntWithParam<P>::guardChecksNodeSig/5 guardChecksNode> {
|
||||
module BarrierGuardWithIntParam<guardChecksNodeSig/4 guardChecksNode> {
|
||||
private predicate ssaDefReachesCertainUse(Definition def, UseImpl use) {
|
||||
exists(SourceVariable v, IRBlock bb, int i |
|
||||
use.hasIndexInBlock(bb, i, v) and
|
||||
@@ -1249,23 +1052,21 @@ module BarrierGuardWithIntParam<ParamSig P, IntWithParam<P>::guardChecksNodeSig/
|
||||
|
||||
private predicate guardChecksInstr(
|
||||
IRGuards::Guards_v1::Guard g, IRGuards::GuardsInput::Expr instr, IRGuards::GuardValue gv,
|
||||
ParamIntPair<P>::TPair pair
|
||||
int indirectionIndex
|
||||
) {
|
||||
exists(Node node, int indirectionIndex, P p |
|
||||
pair = ParamIntPair<P>::MkPair(p, indirectionIndex) and
|
||||
exists(Node node |
|
||||
nodeHasInstruction(node, instr, indirectionIndex) and
|
||||
guardChecksNode(g, node, gv.asBooleanValue(), indirectionIndex, p)
|
||||
guardChecksNode(g, node, gv.asBooleanValue(), indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate guardChecksWithWrappers(
|
||||
DataFlowIntegrationInput::Guard g, SsaImpl::Definition def, IRGuards::GuardValue val,
|
||||
ParamIntPair<P>::MkPair pair
|
||||
int indirectionIndex
|
||||
) {
|
||||
exists(Instruction e, int indirectionIndex |
|
||||
IRGuards::Guards_v1::ParameterizedValidationWrapper<ParamIntPair<P>::TPair, guardChecksInstr/4>::guardChecks(g,
|
||||
e, val, pair) and
|
||||
pair = ParamIntPair<P>::MkPair(_, indirectionIndex)
|
||||
exists(Instruction e |
|
||||
IRGuards::Guards_v1::ParameterizedValidationWrapper<int, guardChecksInstr/4>::guardChecks(g,
|
||||
e, val, indirectionIndex)
|
||||
|
|
||||
indirectionIndex = 0 and
|
||||
def.(Definition).getAUse().getDef() = e
|
||||
@@ -1274,19 +1075,18 @@ module BarrierGuardWithIntParam<ParamSig P, IntWithParam<P>::guardChecksNodeSig/
|
||||
)
|
||||
}
|
||||
|
||||
Node getABarrierNode(int indirectionIndex, P p) {
|
||||
Node getABarrierNode(int indirectionIndex) {
|
||||
// Only get the SynthNodes from the shared implementation, as the ExprNodes cannot
|
||||
// be matched on SourceVariable.
|
||||
result.(SsaSynthNode).getSynthNode() =
|
||||
DataFlowIntegrationImpl::BarrierGuardDefWithState<ParamIntPair<P>::MkPair, guardChecksWithWrappers/4>::getABarrierNode(ParamIntPair<P>::MkPair(p,
|
||||
indirectionIndex))
|
||||
DataFlowIntegrationImpl::BarrierGuardDefWithState<int, guardChecksWithWrappers/4>::getABarrierNode(indirectionIndex)
|
||||
or
|
||||
// Calculate the guarded UseImpls corresponding to ExprNodes directly.
|
||||
exists(
|
||||
DataFlowIntegrationInput::Guard g, IRGuards::GuardValue branch, Definition def, IRBlock bb
|
||||
|
|
||||
guardChecksWithWrappers(g, def, branch, indirectionIndex) and
|
||||
exists(UseImpl use |
|
||||
guardChecksWithWrappers(g, def, branch, ParamIntPair<P>::MkPair(p, indirectionIndex)) and
|
||||
ssaDefReachesCertainUse(def, use) and
|
||||
use.getBlock() = bb and
|
||||
DataFlowIntegrationInput::guardControlsBlock(g, bb, branch) and
|
||||
@@ -1296,16 +1096,15 @@ module BarrierGuardWithIntParam<ParamSig P, IntWithParam<P>::guardChecksNodeSig/
|
||||
}
|
||||
}
|
||||
|
||||
module BarrierGuard<ParamSig P, WithParam<P>::guardChecksNodeSig/4 guardChecksNode> {
|
||||
module BarrierGuard<guardChecksNodeSig/3 guardChecksNode> {
|
||||
private predicate guardChecksNode(
|
||||
IRGuards::IRGuardCondition g, Node e, boolean gv, int indirectionIndex, P p
|
||||
IRGuards::IRGuardCondition g, Node e, boolean branch, int indirectionIndex
|
||||
) {
|
||||
indirectionIndex = 0 and
|
||||
guardChecksNode(g, e, gv, p)
|
||||
guardChecksNode(g, e, branch) and indirectionIndex = 0
|
||||
}
|
||||
|
||||
Node getABarrierNode(P p) {
|
||||
result = BarrierGuardWithIntParam<P, guardChecksNode/5>::getABarrierNode(0, p)
|
||||
Node getABarrierNode() {
|
||||
result = BarrierGuardWithIntParam<guardChecksNode/4>::getABarrierNode(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1360,17 +1159,9 @@ class Definition extends SsaImpl::Definition {
|
||||
private Definition getAPhiInputOrPriorDefinition() {
|
||||
result = this.(PhiNode).getAnInput()
|
||||
or
|
||||
uncertainWriteDefinitionInput(this, result)
|
||||
SsaImpl::uncertainWriteDefinitionInput(this, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this SSA definition is live at the end of basic block `bb`.
|
||||
* That is, this definition reaches the end of basic block `bb`, at which
|
||||
* point it is still live, without crossing another SSA definition of the
|
||||
* same source variable.
|
||||
*/
|
||||
predicate isLiveAtEndOfBlock(IRBlock bb) { ssaDefReachesEndOfBlock(bb, this) }
|
||||
|
||||
/**
|
||||
* Gets a definition that ultimately defines this SSA definition and is
|
||||
* not itself a phi node.
|
||||
|
||||
@@ -4,12 +4,47 @@ import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
private import DataFlowNodes
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
private import DataFlowPrivate
|
||||
private import TypeFlow
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
* Ignored operands are not recognized as uses by SSA, and they don't have a
|
||||
* corresponding `(Indirect)OperandNode`.
|
||||
*/
|
||||
predicate ignoreOperand(Operand operand) {
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
|
||||
operand instanceof MemoryOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an instruction that is not used by the dataflow library.
|
||||
* Ignored instructions are not recognized as reads/writes by SSA, and they
|
||||
* don't have a corresponding `(Indirect)InstructionNode`.
|
||||
*/
|
||||
predicate ignoreInstruction(Instruction instr) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
instr instanceof CallSideEffectInstruction or
|
||||
instr instanceof CallReadSideEffectInstruction or
|
||||
instr instanceof ExitFunctionInstruction or
|
||||
instr instanceof EnterFunctionInstruction or
|
||||
instr instanceof WriteSideEffectInstruction or
|
||||
instr instanceof PhiInstruction or
|
||||
instr instanceof ReadSideEffectInstruction or
|
||||
instr instanceof ChiInstruction or
|
||||
instr instanceof InitializeIndirectionInstruction or
|
||||
instr instanceof AliasedDefinitionInstruction or
|
||||
instr instanceof AliasedUseInstruction or
|
||||
instr instanceof InitializeNonLocalInstruction or
|
||||
instr instanceof ReturnIndirectionInstruction or
|
||||
instr instanceof UninitializedGroupInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of `this` in the member function `f`.
|
||||
* The result is a glvalue if `isGLValue` is true, and
|
||||
@@ -20,6 +55,26 @@ private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) {
|
||||
result.hasType(f.getTypeOfThis(), isGLValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the instruction `i`.
|
||||
*
|
||||
* This is equivalent to `i.getResultLanguageType()` with the exception
|
||||
* of instructions that directly references a `this` IRVariable. In this
|
||||
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
|
||||
* predicate gives the expected type (i.e., a potentially cv-qualified
|
||||
* type `A*` where `A` is the declaring type of the member function that
|
||||
* contains `i`).
|
||||
*/
|
||||
cached
|
||||
CppType getResultLanguageType(Instruction i) {
|
||||
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then
|
||||
if i.isGLValue()
|
||||
then result = getThisType(i.getEnclosingFunction(), true)
|
||||
else result = getThisType(i.getEnclosingFunction(), false)
|
||||
else result = i.getResultLanguageType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the operand `operand`.
|
||||
* This is equivalent to the type of the operand's defining instruction.
|
||||
@@ -292,6 +347,10 @@ predicate isWrite(Node0Impl value, Operand address, boolean certain) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
||||
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
|
||||
}
|
||||
|
||||
newtype TBaseSourceVariable =
|
||||
// Each IR variable gets its own source variable
|
||||
TBaseIRVariable(IRVariable var) or
|
||||
@@ -513,69 +572,6 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
* Ignored operands are not recognized as uses by SSA, and they don't have a
|
||||
* corresponding `(Indirect)OperandNode`.
|
||||
*/
|
||||
cached
|
||||
predicate ignoreOperand(Operand operand) {
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
|
||||
operand instanceof MemoryOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an instruction that is not used by the dataflow library.
|
||||
* Ignored instructions are not recognized as reads/writes by SSA, and they
|
||||
* don't have a corresponding `(Indirect)InstructionNode`.
|
||||
*/
|
||||
cached
|
||||
predicate ignoreInstruction(Instruction instr) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
instr instanceof CallSideEffectInstruction or
|
||||
instr instanceof CallReadSideEffectInstruction or
|
||||
instr instanceof ExitFunctionInstruction or
|
||||
instr instanceof EnterFunctionInstruction or
|
||||
instr instanceof WriteSideEffectInstruction or
|
||||
instr instanceof PhiInstruction or
|
||||
instr instanceof ReadSideEffectInstruction or
|
||||
instr instanceof ChiInstruction or
|
||||
instr instanceof InitializeIndirectionInstruction or
|
||||
instr instanceof AliasedDefinitionInstruction or
|
||||
instr instanceof AliasedUseInstruction or
|
||||
instr instanceof InitializeNonLocalInstruction or
|
||||
instr instanceof ReturnIndirectionInstruction or
|
||||
instr instanceof UninitializedGroupInstruction
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
||||
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the instruction `i`.
|
||||
*
|
||||
* This is equivalent to `i.getResultLanguageType()` with the exception
|
||||
* of instructions that directly references a `this` IRVariable. In this
|
||||
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
|
||||
* predicate gives the expected type (i.e., a potentially cv-qualified
|
||||
* type `A*` where `A` is the declaring type of the member function that
|
||||
* contains `i`).
|
||||
*/
|
||||
cached
|
||||
CppType getResultLanguageType(Instruction i) {
|
||||
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then
|
||||
if i.isGLValue()
|
||||
then result = getThisType(i.getEnclosingFunction(), true)
|
||||
else result = getThisType(i.getEnclosingFunction(), false)
|
||||
else result = i.getResultLanguageType()
|
||||
}
|
||||
|
||||
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
|
||||
private predicate isConversion(Operand op) {
|
||||
exists(Instruction def, Operand use |
|
||||
|
||||
@@ -5,81 +5,64 @@ private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowNodes
|
||||
private import SsaImpl as Ssa
|
||||
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.cpp.ir.dataflow.FlowSteps
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/**
|
||||
* This predicate exists to collapse the `cached` predicates in this module with the
|
||||
* `cached` predicates in other C/C++ dataflow files, which is then collapsed
|
||||
* with the `cached` predicates in `DataFlowImplCommon.qll`.
|
||||
*/
|
||||
cached
|
||||
predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() }
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step. This relation is only used for local taint flow
|
||||
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
|
||||
* special cases that should only apply to local taint flow.
|
||||
*/
|
||||
cached
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// dataflow step
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// taint flow step
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// models-as-data summarized flow for local data flow (i.e. special case for flow
|
||||
// through calls to modeled functions, without relying on global dataflow to join
|
||||
// the dots).
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
|
||||
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
|
||||
model = ""
|
||||
or
|
||||
modeledTaintStep(nodeFrom, nodeTo, model)
|
||||
or
|
||||
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
|
||||
// indirection of the pointer arithmetic instruction. This provides flow from `source`
|
||||
// in `x[source]` to the result of the associated load instruction.
|
||||
exists(PointerArithmeticInstruction pai, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
// object->field conflation for content that is a `TaintInheritingContent`.
|
||||
exists(DataFlow::ContentSet f |
|
||||
readStep(nodeFrom, f, nodeTo) and
|
||||
f.getAReadContent() instanceof TaintInheritingContent
|
||||
) and
|
||||
model = ""
|
||||
}
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step. This relation is only used for local taint flow
|
||||
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
|
||||
* special cases that should only apply to local taint flow.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// dataflow step
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// taint flow step
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// models-as-data summarized flow for local data flow (i.e. special case for flow
|
||||
// through calls to modeled functions, without relying on global dataflow to join
|
||||
// the dots).
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
import Cached
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
|
||||
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
|
||||
model = ""
|
||||
or
|
||||
modeledTaintStep(nodeFrom, nodeTo, model)
|
||||
or
|
||||
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
|
||||
// indirection of the pointer arithmetic instruction. This provides flow from `source`
|
||||
// in `x[source]` to the result of the associated load instruction.
|
||||
exists(PointerArithmeticInstruction pai, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
// object->field conflation for content that is a `TaintInheritingContent`.
|
||||
exists(DataFlow::ContentSet f |
|
||||
readStep(nodeFrom, f, nodeTo) and
|
||||
f.getAReadContent() instanceof TaintInheritingContent
|
||||
) and
|
||||
model = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
@@ -213,7 +196,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
|
||||
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
|
||||
// to that output, but the deref is not modeled in the IR for the caller.
|
||||
exists(
|
||||
CallInstruction call, SideEffectOperandNode indirectArgument, Function func,
|
||||
CallInstruction call, DataFlow::SideEffectOperandNode indirectArgument, Function func,
|
||||
FunctionInput modelIn, FunctionOutput modelOut
|
||||
|
|
||||
indirectArgument = callInput(call, modelIn) and
|
||||
|
||||
@@ -15,7 +15,6 @@ private import TranslatedCall
|
||||
private import TranslatedStmt
|
||||
private import TranslatedFunction
|
||||
private import TranslatedGlobalVar
|
||||
private import TranslatedInitialization
|
||||
|
||||
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
|
||||
instruction = TRawInstruction(result, _)
|
||||
@@ -195,89 +194,6 @@ module Raw {
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression associated with the instruction `instr` that computes
|
||||
* the `Convert` instruction on the extent expression of an allocation.
|
||||
*/
|
||||
cached
|
||||
Expr getAllocationExtentConvertExpr(Instruction instr) {
|
||||
exists(TranslatedNonConstantAllocationSize tas |
|
||||
instr = tas.getInstruction(AllocationExtentConvertTag()) and
|
||||
result = tas.getExtent().getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `ParenthesisExpr` associated with a transparent conversion
|
||||
* instruction, if any.
|
||||
*/
|
||||
cached
|
||||
ParenthesisExpr getTransparentConversionParenthesisExpr(Instruction instr) {
|
||||
exists(TranslatedTransparentConversion ttc |
|
||||
result = ttc.getExpr() and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` belongs to a `TranslatedCoreExpr` that produces an
|
||||
* expression result. This indicates that the instruction represents a
|
||||
* definition whose result should be mapped back to the expression.
|
||||
*/
|
||||
cached
|
||||
predicate instructionProducesExprResult(Instruction instr) {
|
||||
exists(TranslatedCoreExpr tco |
|
||||
tco.getInstruction(_) = instr and
|
||||
tco.producesExprResult()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression associated with a `StoreInstruction` generated
|
||||
* by an `TranslatedAssignOperation`.
|
||||
*/
|
||||
cached
|
||||
Expr getAssignOperationStoreExpr(StoreInstruction store) {
|
||||
exists(TranslatedAssignOperation tao |
|
||||
store = tao.getInstruction(AssignmentStoreTag()) and
|
||||
result = tao.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression associated with a `StoreInstruction` generated
|
||||
* by an `TranslatedCrementOperation`.
|
||||
*/
|
||||
cached
|
||||
Expr getCrementOperationStoreExpr(StoreInstruction store) {
|
||||
exists(TranslatedCrementOperation tco |
|
||||
store = tco.getInstruction(CrementStoreTag()) and
|
||||
result = tco.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `store` is a `StoreInstruction` that defines the temporary
|
||||
* `IRVariable` generated as part of the translation of a ternary expression.
|
||||
*/
|
||||
cached
|
||||
predicate isConditionalExprTempStore(StoreInstruction store) {
|
||||
exists(TranslatedConditionalExpr tce |
|
||||
store = tce.getInstruction(ConditionValueFalseStoreTag())
|
||||
or
|
||||
store = tce.getInstruction(ConditionValueTrueStoreTag())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the instruction that computes the address used to initialize `v`. */
|
||||
cached
|
||||
Instruction getInitializationTargetAddress(IRVariable v) {
|
||||
exists(TranslatedVariableInitialization init |
|
||||
init.getIRVariable() = v and
|
||||
result = init.getTargetAddress()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TStageInstruction = TRawInstruction or TRawUnreachedInstruction;
|
||||
|
||||
@@ -104,11 +104,7 @@ newtype TInstructionTag =
|
||||
} or
|
||||
SizeofVlaDimensionTag(int index) {
|
||||
exists(VlaDeclStmt v | exists(v.getTransitiveVlaDimensionStmt(index)))
|
||||
} or
|
||||
AssertionVarAddressTag() or
|
||||
AssertionVarLoadTag() or
|
||||
AssertionOpTag() or
|
||||
AssertionBranchTag()
|
||||
}
|
||||
|
||||
class InstructionTag extends TInstructionTag {
|
||||
final string toString() { result = getInstructionTagId(this) }
|
||||
@@ -300,12 +296,4 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = CoAwaitBranchTag() and result = "CoAwaitBranch"
|
||||
or
|
||||
tag = BoolToIntConversionTag() and result = "BoolToIntConversion"
|
||||
or
|
||||
tag = AssertionVarAddressTag() and result = "AssertionVarAddress"
|
||||
or
|
||||
tag = AssertionVarLoadTag() and result = "AssertionVarLoad"
|
||||
or
|
||||
tag = AssertionOpTag() and result = "AssertionOp"
|
||||
or
|
||||
tag = AssertionBranchTag() and result = "AssertionBranch"
|
||||
}
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.internal.IRUtilities
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedStmt
|
||||
private import TranslatedFunction
|
||||
|
||||
/**
|
||||
* Holds if `s` is a statement that may be an expanded assertion in a
|
||||
* release build.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate stmtCandidate(Stmt s) {
|
||||
not s.isFromUninstantiatedTemplate(_) and
|
||||
(
|
||||
// The expansion of `__analysis_assume(x != 0);` when `__analysis_assume` is
|
||||
// empty is the empty statement.
|
||||
s instanceof EmptyStmt
|
||||
or
|
||||
// The expansion of `assert(x != 0)` when `assert` is `((void)0)` is a zero literal
|
||||
// with a void type.
|
||||
exists(Expr e |
|
||||
e = s.(ExprStmt).getExpr() and
|
||||
e.getValue() = "0" and
|
||||
e.getActualType() instanceof VoidType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate macroInvocationLocation(int startline, Function f, MacroInvocation mi) {
|
||||
mi.getMacroName() = ["assert", "__analysis_assume"] and
|
||||
mi.getNumberOfArguments() = 1 and
|
||||
mi.getLocation().hasLocationInfo(_, startline, _, _, _) and
|
||||
f.getEntryPoint().isAffectedByMacro(mi)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate stmtParentLocation(int startline, Function f, StmtParent p) {
|
||||
p.getEnclosingFunction() = f and
|
||||
p.getLocation().hasLocationInfo(_, startline, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `mi` is a macro invocation with a name that is known
|
||||
* to correspond to an assertion macro, and the macro invocation
|
||||
* is the only thing on the line.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate assertion0(MacroInvocation mi, Stmt s, string arg) {
|
||||
stmtCandidate(s) and
|
||||
s =
|
||||
unique(StmtParent p, int startline, Function f |
|
||||
macroInvocationLocation(startline, f, mi) and
|
||||
stmtParentLocation(startline, f, p) and
|
||||
// Also do not count the elements from the expanded macro, i.e., when checking
|
||||
// if `assert(x)` is the only thing on the line we do not count the
|
||||
// generated `((void)0)` expression.
|
||||
not p = mi.getAnExpandedElement()
|
||||
|
|
||||
p
|
||||
) and
|
||||
arg = mi.getUnexpandedArgument(0)
|
||||
}
|
||||
|
||||
private Function getEnclosingFunctionForMacroInvocation(MacroInvocation mi) {
|
||||
exists(Stmt s |
|
||||
assertion0(mi, s, _) and
|
||||
result = s.getEnclosingFunction()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` has two components and the `i`'th component of the string
|
||||
* `arg` is `s`, and the components are separated by an operation with
|
||||
* opcode `opcode`.
|
||||
*/
|
||||
bindingset[arg]
|
||||
pragma[inline_late]
|
||||
private predicate parseArgument(string arg, string s, int i, Opcode opcode) {
|
||||
s =
|
||||
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?<=\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
|
||||
i + 1) and
|
||||
opcode instanceof Opcode::CompareLE
|
||||
or
|
||||
s =
|
||||
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?>=\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
|
||||
i + 1) and
|
||||
opcode instanceof Opcode::CompareGE
|
||||
or
|
||||
s =
|
||||
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?<\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
|
||||
i + 1) and
|
||||
opcode instanceof Opcode::CompareLT
|
||||
or
|
||||
s =
|
||||
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?>\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
|
||||
i + 1) and
|
||||
opcode instanceof Opcode::CompareGT
|
||||
or
|
||||
s =
|
||||
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?!=\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
|
||||
i + 1) and
|
||||
opcode instanceof Opcode::CompareNE
|
||||
or
|
||||
s =
|
||||
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?==\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
|
||||
i + 1) and
|
||||
opcode instanceof Opcode::CompareEQ
|
||||
}
|
||||
|
||||
private Element getAChildScope(Element scope) { result.getParentScope() = scope }
|
||||
|
||||
private predicate hasAVariable(MacroInvocation mi, Stmt s, Element scope) {
|
||||
assertion0(mi, s, _) and
|
||||
s.getParent() = scope
|
||||
or
|
||||
hasAVariable(mi, s, getAChildScope(scope))
|
||||
}
|
||||
|
||||
private LocalScopeVariable getVariable(MacroInvocation mi, int i) {
|
||||
exists(string operand, string arg, Stmt s |
|
||||
assertion0(mi, s, arg) and
|
||||
parseArgument(arg, operand, i, _) and
|
||||
result =
|
||||
unique(Variable v |
|
||||
v.getLocation().getStartLine() < s.getLocation().getStartLine() and
|
||||
hasAVariable(mi, s, v.getParentScope()) and
|
||||
v.hasName(operand)
|
||||
|
|
||||
v
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th component of the macro invocation `mi` with opcode
|
||||
* `opcode` is a reference to `var`.
|
||||
*/
|
||||
private predicate hasVarAccessMacroArgument(MacroInvocation mi, Variable var, int i, Opcode opcode) {
|
||||
exists(string arg, string s, Function f |
|
||||
arg = mi.getUnexpandedArgument(0) and
|
||||
f = getEnclosingFunctionForMacroInvocation(mi) and
|
||||
parseArgument(arg, s, i, opcode) and
|
||||
var = getVariable(mi, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th component of the macro invocation `mi` with opcode
|
||||
* `opcode` is a constant with the value `k`.
|
||||
*/
|
||||
private predicate hasConstMacroArgument(MacroInvocation mi, int k, int i, Opcode opcode) {
|
||||
exists(string arg, string s |
|
||||
assertion0(mi, _, arg) and
|
||||
s.toInt() = k and
|
||||
parseArgument(arg, s, i, opcode)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasAssertionOperand(MacroInvocation mi, int i) {
|
||||
hasVarAccessMacroArgument(mi, _, i, _)
|
||||
or
|
||||
hasConstMacroArgument(mi, _, i, _)
|
||||
}
|
||||
|
||||
private predicate hasAssertionOpcode(MacroInvocation mi, Opcode opcode) {
|
||||
hasVarAccessMacroArgument(mi, _, _, opcode)
|
||||
or
|
||||
hasConstMacroArgument(mi, _, _, opcode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `mi` is a macro invocation that is an assertion that should be generated
|
||||
* in the control-flow graph at `s`.
|
||||
*/
|
||||
predicate assertion(MacroInvocation mi, Stmt s) {
|
||||
assertion0(mi, s, _) and
|
||||
hasAssertionOperand(mi, 0) and
|
||||
hasAssertionOperand(mi, 1)
|
||||
}
|
||||
|
||||
/** The translation of an operand of an assertion. */
|
||||
abstract private class TranslatedAssertionOperand extends TranslatedElement,
|
||||
TTranslatedAssertionOperand
|
||||
{
|
||||
MacroInvocation mi;
|
||||
int index;
|
||||
|
||||
TranslatedAssertionOperand() { this = TTranslatedAssertionOperand(mi, index) }
|
||||
|
||||
MacroInvocation getMacroInvocation() { result = mi }
|
||||
|
||||
/**
|
||||
* Gets the statement that is being replaced by the assertion that uses this
|
||||
* operand.
|
||||
*/
|
||||
Stmt getStmt() { assertion(mi, result) }
|
||||
|
||||
final override Locatable getAst() { result = this.getStmt() }
|
||||
|
||||
final override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
final override Declaration getFunction() { result = this.getStmt().getEnclosingFunction() }
|
||||
|
||||
/** Gets the instruction which holds the result of this operand. */
|
||||
abstract Instruction getResult();
|
||||
|
||||
final override string toString() { result = "Operand of assertion: " + mi }
|
||||
|
||||
/** Gets the index of this operand (i.e., `0` or `1`). */
|
||||
final int getIndex() { result = index }
|
||||
}
|
||||
|
||||
/** An operand of an assertion that is a variable access. */
|
||||
class TranslatedAssertionVarAccess extends TranslatedAssertionOperand {
|
||||
TranslatedAssertionVarAccess() { hasVarAccessMacroArgument(mi, _, index, _) }
|
||||
|
||||
Variable getVariable() { hasVarAccessMacroArgument(mi, result, index, _) }
|
||||
|
||||
final override IRUserVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AssertionVarAddressTag() and
|
||||
result.getVariable() = this.getVariable()
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getInstruction(AssertionVarAddressTag()) and kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = AssertionVarAddressTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(AssertionVarLoadTag())
|
||||
or
|
||||
tag = AssertionVarLoadTag() and
|
||||
result = getTranslatedAssertionMacroInvocation(mi).getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
final override Instruction getALastInstructionInternal() {
|
||||
result = this.getInstruction(AssertionVarLoadTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
exists(Variable v | v = this.getVariable() |
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
tag = AssertionVarAddressTag() and
|
||||
resultType = getTypeForGLValue(v.getType())
|
||||
or
|
||||
opcode instanceof Opcode::Load and
|
||||
tag = AssertionVarLoadTag() and
|
||||
resultType = getTypeForPRValue(v.getType())
|
||||
)
|
||||
}
|
||||
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = AssertionVarLoadTag() and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(AssertionVarAddressTag())
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = this.getInstruction(AssertionVarLoadTag()) }
|
||||
}
|
||||
|
||||
/** An operand of an assertion that is a constant access. */
|
||||
private class TranslatedAssertionConst extends TranslatedAssertionOperand {
|
||||
TranslatedAssertionConst() { hasConstMacroArgument(mi, _, index, _) }
|
||||
|
||||
int getConst() { hasConstMacroArgument(mi, result, index, _) }
|
||||
|
||||
final override string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = this.getConst().toString()
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getInstruction(OnlyInstructionTag()) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedAssertionMacroInvocation(mi).getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
final override Instruction getALastInstructionInternal() {
|
||||
result = this.getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
opcode instanceof Opcode::Constant and
|
||||
tag = OnlyInstructionTag() and
|
||||
resultType = getIntType()
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedAssertionMacroInvocation` corresponding to the macro
|
||||
* invocation `mi`.
|
||||
*/
|
||||
TranslatedAssertionMacroInvocation getTranslatedAssertionMacroInvocation(MacroInvocation mi) {
|
||||
result.getMacroInvocation() = mi
|
||||
}
|
||||
|
||||
/**
|
||||
* A synthesized assertion which would have otherwise been invisible because the
|
||||
* database represents a release build where assertions are disabled.
|
||||
*/
|
||||
private class TranslatedAssertionMacroInvocation extends TranslatedStmt {
|
||||
MacroInvocation mi;
|
||||
|
||||
TranslatedAssertionMacroInvocation() { assertion(mi, stmt) }
|
||||
|
||||
final override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getLeft().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
TranslatedAssertionOperand getLeft() {
|
||||
result.getMacroInvocation() = mi and
|
||||
result.getIndex() = 0
|
||||
}
|
||||
|
||||
TranslatedAssertionOperand getRight() {
|
||||
result.getMacroInvocation() = mi and
|
||||
result.getIndex() = 1
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = AssertionOpTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(AssertionBranchTag())
|
||||
or
|
||||
tag = AssertionBranchTag() and
|
||||
kind instanceof TrueEdge and
|
||||
result = this.getParent().getChildSuccessor(this, _)
|
||||
}
|
||||
|
||||
final override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and result = this.getLeft()
|
||||
or
|
||||
id = 1 and result = this.getRight()
|
||||
}
|
||||
|
||||
final override Instruction getALastInstructionInternal() {
|
||||
result = this.getInstruction(AssertionBranchTag())
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = AssertionOpTag() and
|
||||
resultType = getBoolType() and
|
||||
hasAssertionOpcode(mi, opcode)
|
||||
or
|
||||
tag = AssertionBranchTag() and
|
||||
resultType = getVoidType() and
|
||||
opcode instanceof Opcode::ConditionalBranch
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getLeft() and
|
||||
result = this.getRight().getFirstInstruction(kind)
|
||||
or
|
||||
child = this.getRight() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(AssertionOpTag())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = AssertionOpTag() and
|
||||
(
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = this.getLeft().getResult()
|
||||
or
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = this.getRight().getResult()
|
||||
)
|
||||
or
|
||||
tag = AssertionBranchTag() and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = this.getInstruction(AssertionOpTag())
|
||||
}
|
||||
|
||||
MacroInvocation getMacroInvocation() { result = mi }
|
||||
}
|
||||
@@ -12,7 +12,6 @@ private import TranslatedFunction
|
||||
private import TranslatedStmt
|
||||
private import TranslatedExpr
|
||||
private import IRConstruction
|
||||
private import TranslatedAssertion
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import SideEffects
|
||||
|
||||
@@ -139,14 +138,6 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
// conditionally constructed (until we have a mechanism for calling these only when the
|
||||
// temporary's constructor was run)
|
||||
isConditionalTemporaryDestructorCall(expr)
|
||||
or
|
||||
// An assertion in a release build is often defined as `#define assert(x) ((void)0)`.
|
||||
// We generate a synthetic assertion in release builds, and when we do that the
|
||||
// expression `((void)0)` should not be translated.
|
||||
exists(MacroInvocation mi |
|
||||
assertion(mi, _) and
|
||||
expr = mi.getExpr().getFullyConverted()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -918,8 +909,7 @@ newtype TTranslatedElement =
|
||||
} or
|
||||
// The side effect that initializes newly-allocated memory.
|
||||
TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } or
|
||||
TTranslatedStaticStorageDurationVarInit(Variable var) { Raw::varHasIRFunc(var) } or
|
||||
TTranslatedAssertionOperand(MacroInvocation mi, int index) { hasAssertionOperand(mi, index) }
|
||||
TTranslatedStaticStorageDurationVarInit(Variable var) { Raw::varHasIRFunc(var) }
|
||||
|
||||
/**
|
||||
* Gets the index of the first explicitly initialized element in `initList`
|
||||
|
||||
@@ -10,7 +10,6 @@ private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedFunction
|
||||
private import TranslatedInitialization
|
||||
private import TranslatedAssertion
|
||||
|
||||
TranslatedStmt getTranslatedStmt(Stmt stmt) { result.getAst() = stmt }
|
||||
|
||||
@@ -325,16 +324,8 @@ abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
|
||||
|
||||
class TranslatedEmptyStmt extends TranslatedStmt {
|
||||
TranslatedEmptyStmt() {
|
||||
// An assertion macro invocation can expand to
|
||||
// an empty statement in release builds. In that case
|
||||
// we synthesize the check that would have occurred.
|
||||
// This is handled by `TranslatedAssertion.qll` and so
|
||||
// we exclude these statements here.
|
||||
not assertion(_, stmt) and
|
||||
stmt instanceof EmptyStmt
|
||||
or
|
||||
stmt instanceof LabelStmt
|
||||
or
|
||||
stmt instanceof EmptyStmt or
|
||||
stmt instanceof LabelStmt or
|
||||
stmt instanceof SwitchCase
|
||||
}
|
||||
|
||||
@@ -390,7 +381,7 @@ class TranslatedDeclStmt extends TranslatedStmt {
|
||||
|
||||
override TranslatedElement getLastChild() { result = this.getChild(this.getChildCount() - 1) }
|
||||
|
||||
private int getChildCount() { result = count(int i | exists(this.getDeclarationEntry(i))) }
|
||||
private int getChildCount() { result = count(this.getDeclarationEntry(_)) }
|
||||
|
||||
IRDeclarationEntry getIRDeclarationEntry(int index) {
|
||||
result.hasIndex(index) and
|
||||
@@ -429,15 +420,6 @@ class TranslatedDeclStmt extends TranslatedStmt {
|
||||
class TranslatedExprStmt extends TranslatedStmt {
|
||||
override ExprStmt stmt;
|
||||
|
||||
TranslatedExprStmt() {
|
||||
// An assertion macro invocation typically expand to the
|
||||
// expression `((void)0)` in release builds. In that case
|
||||
// we synthesize the check that would have occurred.
|
||||
// This is handled by `TranslatedAssertion.qll` and so
|
||||
// we exclude these statements here.
|
||||
not assertion(_, stmt)
|
||||
}
|
||||
|
||||
TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr().getFullyConverted()) }
|
||||
|
||||
override TranslatedElement getChildInternal(int id) { id = 0 and result = this.getExpr() }
|
||||
|
||||
@@ -57,4 +57,3 @@ private import implementations.CAtlFile
|
||||
private import implementations.CAtlFileMapping
|
||||
private import implementations.CAtlTemporaryFile
|
||||
private import implementations.CRegKey
|
||||
private import implementations.WinHttp
|
||||
|
||||
@@ -16,3 +16,17 @@ private class MySqlExecutionFunction extends SqlExecutionFunction {
|
||||
|
||||
override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `mysql_real_escape_string` family of functions from the MySQL C API.
|
||||
*/
|
||||
private class MySqlBarrierFunction extends SqlBarrierFunction {
|
||||
MySqlBarrierFunction() {
|
||||
this.hasName(["mysql_real_escape_string", "mysql_real_escape_string_quote"])
|
||||
}
|
||||
|
||||
override predicate barrierSqlArgument(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(2) and
|
||||
output.isParameterDeref(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.dataflow.FlowSteps
|
||||
private import semmle.code.cpp.dataflow.new.DataFlow
|
||||
|
||||
/** The `WINHTTP_HEADER_NAME` class from `winhttp.h`. */
|
||||
class WinHttpHeaderName extends Class {
|
||||
WinHttpHeaderName() { this.hasGlobalName("_WINHTTP_HEADER_NAME") }
|
||||
}
|
||||
|
||||
/** The `WINHTTP_EXTENDED_HEADER` class from `winhttp.h`. */
|
||||
class WinHttpExtendedHeader extends Class {
|
||||
WinHttpExtendedHeader() { this.hasGlobalName("_WINHTTP_EXTENDED_HEADER") }
|
||||
}
|
||||
|
||||
private class WinHttpHeaderNameInheritingContent extends TaintInheritingContent,
|
||||
DataFlow::FieldContent
|
||||
{
|
||||
WinHttpHeaderNameInheritingContent() {
|
||||
this.getIndirectionIndex() = 2 and
|
||||
(
|
||||
this.getAField().getDeclaringType() instanceof WinHttpHeaderName
|
||||
or
|
||||
// The extended header looks like:
|
||||
// struct WINHTTP_EXTENDED_HEADER {
|
||||
// union { [...] };
|
||||
// union { [...] };
|
||||
// };
|
||||
// So the first declaring type is the anonymous unions, and the declaring
|
||||
// type of those anonymous unions is the `WINHTTP_EXTENDED_HEADER` struct.
|
||||
this.getAField().getDeclaringType().getDeclaringType() instanceof WinHttpExtendedHeader
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** The `URL_COMPONENTS` class from `winhttp.h`. */
|
||||
class WinHttpUrlComponents extends Class {
|
||||
WinHttpUrlComponents() { this.hasGlobalName("_WINHTTP_URL_COMPONENTS") }
|
||||
}
|
||||
|
||||
private class WinHttpUrlComponentsInheritingContent extends TaintInheritingContent,
|
||||
DataFlow::FieldContent
|
||||
{
|
||||
WinHttpUrlComponentsInheritingContent() {
|
||||
exists(Field f | f = this.getField() and f.getDeclaringType() instanceof WinHttpUrlComponents |
|
||||
if f.getType().getUnspecifiedType() instanceof PointerType
|
||||
then this.getIndirectionIndex() = 2
|
||||
else this.getIndirectionIndex() = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -404,7 +404,7 @@ predicate cmpWithLinearBound(
|
||||
* For example, if `t` is a signed 32-bit type then holds if `lb` is
|
||||
* `-2^31` and `ub` is `2^31 - 1`.
|
||||
*/
|
||||
private predicate typeBounds0(ArithmeticType t, float lb, float ub) {
|
||||
private predicate typeBounds(ArithmeticType t, float lb, float ub) {
|
||||
exists(IntegralType integralType, float limit |
|
||||
integralType = t and limit = 2.pow(8 * integralType.getSize())
|
||||
|
|
||||
@@ -423,42 +423,6 @@ private predicate typeBounds0(ArithmeticType t, float lb, float ub) {
|
||||
t instanceof FloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying type for an enumeration `e`.
|
||||
*
|
||||
* If the enumeration does not have an explicit type we approximate it using
|
||||
* the following rules:
|
||||
* - The result type is always `signed`, and
|
||||
* - if the largest value fits in an `int` the result is `int`. Otherwise, the
|
||||
* result is `long`.
|
||||
*/
|
||||
private IntegralType getUnderlyingTypeForEnum(Enum e) {
|
||||
result = e.getExplicitUnderlyingType()
|
||||
or
|
||||
not e.hasExplicitUnderlyingType() and
|
||||
result.isSigned() and
|
||||
exists(IntType intType |
|
||||
if max(e.getAnEnumConstant().getValue().toFloat()) >= 2.pow(8 * intType.getSize() - 1)
|
||||
then result instanceof LongType
|
||||
else result = intType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `lb` and `ub` are the lower and upper bounds of the unspecified
|
||||
* type `t`.
|
||||
*
|
||||
* For example, if `t` is a signed 32-bit type then holds if `lb` is
|
||||
* `-2^31` and `ub` is `2^31 - 1`.
|
||||
*
|
||||
* Unlike `typeBounds0`, this predicate also handles `Enum` types.
|
||||
*/
|
||||
private predicate typeBounds(Type t, float lb, float ub) {
|
||||
typeBounds0(t, lb, ub)
|
||||
or
|
||||
typeBounds0(getUnderlyingTypeForEnum(t), lb, ub)
|
||||
}
|
||||
|
||||
private Type stripReference(Type t) {
|
||||
if t instanceof ReferenceType then result = t.(ReferenceType).getBaseType() else result = t
|
||||
}
|
||||
|
||||
@@ -512,8 +512,8 @@ private module BoundsEstimate {
|
||||
*/
|
||||
float getBoundsLimit() {
|
||||
// This limit is arbitrary, but low enough that it prevents timeouts on
|
||||
// specific observed customer databases (and in the tests).
|
||||
result = 2.0.pow(29)
|
||||
// specific observed customer databases (and the in the tests).
|
||||
result = 2.0.pow(40)
|
||||
}
|
||||
|
||||
/** Gets the maximum number of bounds possible for `t` when widening is used. */
|
||||
@@ -552,47 +552,34 @@ private module BoundsEstimate {
|
||||
private float nrOfBoundsPhiGuard(RangeSsaDefinition def, StackVariable v) {
|
||||
// If we have
|
||||
//
|
||||
// if (x < c) { e1 } else { e2 }
|
||||
// e3
|
||||
//
|
||||
// then `{ e1 }` and `{ e2 }` are both guard phi nodes guarded by `x < c`.
|
||||
// The range analysis propagates bounds on `x` into both branches, filtered
|
||||
// by the condition. In this case all lower bounds flow to `{ e1 }` and only
|
||||
// lower bounds that are smaller than `c` flow to `{ e2 }`.
|
||||
//
|
||||
// The largest number of bounds possible for `e3` is the number of bounds on `x` plus
|
||||
// one. This happens when all bounds flow from `x` to `e1` to `e3` and the
|
||||
// bound `c` can flow to `e2` to `e3`.
|
||||
//
|
||||
// We want to optimize our bounds estimate for `e3`, as that is the estimate
|
||||
// that can continue propagating forward. We don't know how the existing
|
||||
// bounds will be split between the different branches. That depends on
|
||||
// whether the range analysis is tracking lower bounds or upper bounds, and
|
||||
// on the meaning of the condition.
|
||||
//
|
||||
// As a heuristic we divide the number of bounds on `x` by 2 to "average"
|
||||
// the effect of the condition and add 1 to account for the bound from the
|
||||
// condition itself. This will approximate estimates inside the branches,
|
||||
// but will give a good estimate after the branches are merged.
|
||||
//
|
||||
// This also handles cases such as this one
|
||||
//
|
||||
// if (x < c) { e1 }
|
||||
// e3
|
||||
// e2
|
||||
//
|
||||
// where `e3` is both a guard phi node (guarded by `x < c`) and a normal
|
||||
// phi node (control is merged after the `if` statement). Here half of the
|
||||
// bounds flow into the branch and then to `e3` as a normal phi node and the
|
||||
// "other" half flow from the condition to `e3` as a guard phi node.
|
||||
exists(float varBounds |
|
||||
// If there's different `access`es, then they refer to the same
|
||||
// variable with the same lower bounds. Hence adding these guards makes no
|
||||
// sense (the implementation will take the union, but they'll be removed by
|
||||
// deduplication). Hence we use `max` as an approximation.
|
||||
varBounds =
|
||||
max(VariableAccess access | isGuardPhiWithBound(def, v, access) | nrOfBoundsExpr(access)) and
|
||||
result = (varBounds + 1) / 2
|
||||
)
|
||||
// then `e2` is both a guard phi node (guarded by `x < c`) and a normal
|
||||
// phi node (control is merged after the `if` statement).
|
||||
//
|
||||
// Assume `x` has `n` bounds. Then `n` bounds are propagated to the guard
|
||||
// phi node `{ e1 }` and, since `{ e1 }` is input to `e2` as a normal phi
|
||||
// node, `n` bounds are propagated to `e2`. If we also propagate the `n`
|
||||
// bounds to `e2` as a guard phi node, then we square the number of
|
||||
// bounds.
|
||||
//
|
||||
// However in practice `x < c` is going to cut down the number of bounds:
|
||||
// The tracked bounds can't flow to both branches as that would require
|
||||
// them to simultaneously be greater and smaller than `c`. To approximate
|
||||
// this better, the contribution from a guard phi node that is also a
|
||||
// normal phi node is 1.
|
||||
exists(def.getAPhiInput(v)) and
|
||||
isGuardPhiWithBound(def, v, _) and
|
||||
result = 1
|
||||
or
|
||||
not exists(def.getAPhiInput(v)) and
|
||||
// If there's different `access`es, then they refer to the same variable
|
||||
// with the same lower bounds. Hence adding these guards make no sense (the
|
||||
// implementation will take the union, but they'll be removed by
|
||||
// deduplication). Hence we use `max` as an approximation.
|
||||
result =
|
||||
max(VariableAccess access | isGuardPhiWithBound(def, v, access) | nrOfBoundsExpr(access))
|
||||
or
|
||||
def.isPhiNode(v) and
|
||||
not isGuardPhiWithBound(def, v, _) and
|
||||
@@ -2193,16 +2180,6 @@ module SimpleRangeAnalysisInternal {
|
||||
|
||||
/** Gets the estimate of the number of bounds for `e`. */
|
||||
float estimateNrOfBounds(Expr e) { result = BoundsEstimate::nrOfBoundsExpr(e) }
|
||||
|
||||
/** Counts the numbers of lower bounds that are computed internally for `e`. */
|
||||
float countNrOfLowerBounds(Expr e) {
|
||||
result = strictcount(float lb | lb = getLowerBoundsImpl(e) | lb)
|
||||
}
|
||||
|
||||
/** Counts the numbers of upper bounds that are computed internally for `e`. */
|
||||
float countNrOfUpperBounds(Expr e) {
|
||||
result = strictcount(float ub | ub = getUpperBoundsImpl(e) | ub)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates for debugging the simple range analysis library. */
|
||||
@@ -2231,7 +2208,7 @@ private module Debug {
|
||||
*/
|
||||
predicate countGetLowerBoundsImpl(Expr e, int n) {
|
||||
e = getRelevantLocatable() and
|
||||
n = SimpleRangeAnalysisInternal::countNrOfLowerBounds(e)
|
||||
n = strictcount(float lb | lb = getLowerBoundsImpl(e) | lb)
|
||||
}
|
||||
|
||||
float debugNrOfBounds(Expr e) {
|
||||
|
||||
@@ -20,7 +20,7 @@ class Stmt extends StmtParent, @stmt {
|
||||
predicate hasChild(Element e, int n) { this.getChild(n) = e }
|
||||
|
||||
/** Gets the enclosing function of this statement, if any. */
|
||||
override Function getEnclosingFunction() { result = stmtEnclosingElement(this) }
|
||||
Function getEnclosingFunction() { result = stmtEnclosingElement(this) }
|
||||
|
||||
/**
|
||||
* Gets the nearest enclosing block of this statement in the source, if any.
|
||||
@@ -159,10 +159,7 @@ private class TStmtParent = @stmt or @expr;
|
||||
*
|
||||
* This is normally a statement, but may be a `StmtExpr`.
|
||||
*/
|
||||
class StmtParent extends ControlFlowNode, TStmtParent {
|
||||
/** Gets the enclosing function of this element, if any. */
|
||||
Function getEnclosingFunction() { none() }
|
||||
}
|
||||
class StmtParent extends ControlFlowNode, TStmtParent { }
|
||||
|
||||
/**
|
||||
* A C/C++ 'expression' statement.
|
||||
|
||||
@@ -236,62 +236,6 @@ extractor_version(
|
||||
string frontend_version: string ref
|
||||
)
|
||||
|
||||
/**
|
||||
* Gives the TRAP filename that `trap` is associated with.
|
||||
* For debugging only.
|
||||
*/
|
||||
trap_filename(
|
||||
int trap: @trap,
|
||||
string filename: string ref
|
||||
);
|
||||
|
||||
/**
|
||||
* Gives the tag name for `tag`.
|
||||
* For debugging only.
|
||||
*/
|
||||
tag_name(
|
||||
int tag: @tag,
|
||||
string name: string ref
|
||||
);
|
||||
|
||||
@trap_or_tag = @tag | @trap;
|
||||
|
||||
/**
|
||||
* Gives the name for the source file.
|
||||
*/
|
||||
source_file_name(
|
||||
int sf: @source_file,
|
||||
string name: string ref
|
||||
);
|
||||
|
||||
/**
|
||||
* In `build-mode: none` overlay mode, indicates that `source_file`
|
||||
* (`/path/to/foo.c`) uses the TRAP file `trap_file`; i.e. it is the
|
||||
* TRAP file corresponding to `foo.c`, something it transitively
|
||||
* includes, or a template instantiation it transitively uses.
|
||||
*/
|
||||
source_file_uses_trap(
|
||||
int source_file: @source_file ref,
|
||||
int trap_file: @trap ref
|
||||
);
|
||||
|
||||
/**
|
||||
* In `build-mode: none` overlay mode, indicates that the TRAP file
|
||||
* `trap_file` uses tag `tag`.
|
||||
*/
|
||||
trap_uses_tag(
|
||||
int trap_file: @trap ref,
|
||||
int tag: @tag ref
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if there is a definition of `element` in TRAP file or tag `t`.
|
||||
*/
|
||||
in_trap_or_tag(
|
||||
int element: @element ref,
|
||||
int t: @trap_or_tag ref
|
||||
);
|
||||
|
||||
pch_uses(
|
||||
int pch: @pch ref,
|
||||
int compilation: @compilation ref,
|
||||
@@ -2409,7 +2353,6 @@ case @preprocdirect.kind of
|
||||
| 14 = @ppd_ms_import
|
||||
| 15 = @ppd_elifdef
|
||||
| 16 = @ppd_elifndef
|
||||
| 17 = @ppd_embed
|
||||
| 18 = @ppd_warning
|
||||
;
|
||||
|
||||
@@ -2436,11 +2379,6 @@ includes(
|
||||
int included: @file ref
|
||||
);
|
||||
|
||||
embeds(
|
||||
unique int id: @ppd_embed ref,
|
||||
int included: @file ref
|
||||
);
|
||||
|
||||
link_targets(
|
||||
int id: @link_target,
|
||||
int binary: @file ref
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Trap extends @trap {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from Element e, Trap trap
|
||||
where in_trap(e, trap)
|
||||
select e, trap
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,22 +0,0 @@
|
||||
newtype TSourceFile = MkSourceFile(string name) { source_file_uses_trap(name, _) }
|
||||
|
||||
module FreshSourceFile = QlBuiltins::NewEntity<TSourceFile>;
|
||||
|
||||
class SourceFile extends FreshSourceFile::EntityId {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Trap extends @trap {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
query predicate mk_source_file_name(SourceFile source_file, string name) {
|
||||
source_file = FreshSourceFile::map(MkSourceFile(name))
|
||||
}
|
||||
|
||||
query predicate mk_source_file_uses_trap(SourceFile source_file, Trap trap) {
|
||||
exists(string name |
|
||||
source_file_uses_trap(name, trap) and
|
||||
mk_source_file_name(source_file, name)
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
description: Add source_file_name
|
||||
compatibility: backwards
|
||||
source_file_uses_trap.rel: run source_files.ql mk_source_file_uses_trap
|
||||
source_file_name.rel: run source_files.ql mk_source_file_name
|
||||
in_trap.rel: delete
|
||||
in_trap_or_tag.rel: run in_trap_or_tag.ql
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user