Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions[bot]
9f0b0fa81c update codeql documentation 2026-01-20 22:58:44 +00:00
1286 changed files with 108808 additions and 202800 deletions

View File

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

View File

@@ -1 +1 @@
9.0.0
8.4.2

View File

@@ -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 ###

View File

@@ -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

View File

@@ -15,22 +15,20 @@ 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_nodejs", version = "6.2.0-codeql.1")
bazel_dep(name = "rules_python", version = "0.40.0")
bazel_dep(name = "rules_shell", version = "0.5.0")
bazel_dep(name = "bazel_skylib", version = "1.8.1")
bazel_dep(name = "abseil-cpp", version = "20240116.1", repo_name = "absl")
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
bazel_dep(name = "fmt", version = "12.1.0-codeql.1")
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
bazel_dep(name = "gazelle", version = "0.47.0")
bazel_dep(name = "rules_kotlin", version = "2.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 = "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")

View File

@@ -1,13 +1,3 @@
## 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

View File

@@ -1,5 +0,0 @@
## 0.4.27
### Bug Fixes
* Fixed a crash when analysing a `${{ ... }}` expression over around 300 characters in length.

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.4.28
lastReleaseVersion: 0.4.26

View File

@@ -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()
}

View File

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

View File

@@ -1,11 +1,3 @@
## 0.6.20
No user-facing changes.
## 0.6.19
No user-facing changes.
## 0.6.18
No user-facing changes.

View File

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

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.20
lastReleaseVersion: 0.6.18

View File

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

View File

@@ -1,5 +0,0 @@
import codeql.actions.ast.internal.Ast
int getAnExpressionLength() { result = any(ExpressionImpl e).toString().length() }
select max(getAnExpressionLength())

View File

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

View File

@@ -1,2 +0,0 @@
description: Sections for databaseMetadata and overlayChangedFiles
compatibility: full

View File

@@ -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

View File

@@ -1,4 +0,0 @@
description: Support embed preprocessor directive
compatibility: partial
embeds.rel: delete
preprocdirects.rel: run preprocdirects.qlo

View File

@@ -1,27 +1,3 @@
## 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

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* 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.

View File

@@ -1,4 +0,0 @@
---
category: fix
---
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
* `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.

View File

@@ -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.

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 7.1.1
lastReleaseVersion: 7.0.0

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -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"]

View File

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

View File

@@ -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

View File

@@ -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_

View File

@@ -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)) }
}

View File

@@ -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

View File

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

View File

@@ -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())
}
/**

View File

@@ -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, _, _, _, _, _)
}
@@ -1031,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
@@ -1125,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
) {
@@ -1145,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, _)
}
}

View File

@@ -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 =

View File

@@ -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() }

View File

@@ -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))
)
}

View File

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

View File

@@ -1144,7 +1144,7 @@ private newtype TDataFlowCall =
}
private predicate summarizedCallableIsManual(SummarizedCallable sc) {
sc.asSummarizedCallable().hasManualModel()
sc.asSummarizedCallable().applyManualModel()
}
/**

View File

@@ -312,13 +312,6 @@ class Node extends TIRDataFlowNode {
*/
Expr asDefinition() { result = this.asDefinition(_) }
private predicate isCertainStore() {
exists(SsaImpl::Definition def |
SsaImpl::defToNode(this, def, _) and
def.isCertain()
)
}
/**
* Gets the definition associated with this node, if any.
*
@@ -368,10 +361,11 @@ class Node extends TIRDataFlowNode {
* pointed to by `p`.
*/
Expr asDefinition(boolean uncertain) {
exists(StoreInstruction store |
exists(StoreInstruction store, SsaImpl::Definition def |
store = this.asInstruction() and
result = asDefinitionImpl(store) and
if this.isCertainStore() then uncertain = false else uncertain = true
SsaImpl::defToNode(this, def, _) and
if def.isCertain() then uncertain = false else uncertain = true
)
}
@@ -1726,7 +1720,9 @@ private module Cached {
SsaImpl::ssaFlow(n, succ) and
bb1 = n.getBasicBlock() and
bb2 = succ.getBasicBlock() and
bb2.strictlyDominates(bb1)
bb1 != bb2 and
bb2.dominates(bb1) and
bb1.getASuccessor+() = bb2
)
}
@@ -2421,19 +2417,6 @@ class ContentSet instanceof Content {
}
}
private signature class ParamSig;
private module WithParam<ParamSig P> {
/**
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
*
* The expression `e` is expected to be a syntactic part of the guard `g`.
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
* the argument `x`.
*/
signature predicate guardChecksSig(IRGuardCondition g, Expr e, boolean branch, P param);
}
/**
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
*
@@ -2455,7 +2438,7 @@ private predicate controls(IRGuardCondition g, Node n, boolean edge) {
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
* in data flow and taint tracking.
*/
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
module BarrierGuard<guardChecksSig/3 guardChecks> {
bindingset[value, n]
pragma[inline_late]
private predicate convertedExprHasValueNumber(ValueNumber value, Node n) {
@@ -2465,13 +2448,12 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
)
}
private predicate guardChecksNode(IRGuardCondition g, Node n, boolean branch, P p) {
guardChecks(g, n.asOperand().getDef().getConvertedResultExpression(), branch, p)
private predicate guardChecksNode(IRGuardCondition g, Node n, boolean branch) {
guardChecks(g, n.asOperand().getDef().getConvertedResultExpression(), branch)
}
/**
* Gets an expression node that is safely guarded by the given guard check
* when the parameter is `p`.
* Gets an expression node that is safely guarded by the given guard check.
*
* For example, given the following code:
* ```cpp
@@ -2502,27 +2484,19 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
*
* NOTE: If an indirect expression is tracked, use `getAnIndirectBarrierNode` instead.
*/
Node getABarrierNode(P p) {
Node getABarrierNode() {
exists(IRGuardCondition g, ValueNumber value, boolean edge |
convertedExprHasValueNumber(value, result) and
guardChecks(g,
pragma[only_bind_into](value.getAnInstruction().getConvertedResultExpression()), edge, p) and
pragma[only_bind_into](value.getAnInstruction().getConvertedResultExpression()), edge) and
controls(g, result, edge)
)
or
result = SsaImpl::BarrierGuard<P, guardChecksNode/4>::getABarrierNode(p)
result = SsaImpl::BarrierGuard<guardChecksNode/3>::getABarrierNode()
}
/**
* Gets an expression node that is safely guarded by the given guard check.
*
* See `getABarrierNode/1` for examples.
*/
Node getABarrierNode() { result = getABarrierNode(_) }
/**
* Gets an indirect expression node that is safely guarded by the given
* guard check with parameter `p`.
* Gets an indirect expression node that is safely guarded by the given guard check.
*
* For example, given the following code:
* ```cpp
@@ -2554,13 +2528,6 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
*
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
*/
Node getAnIndirectBarrierNode(P p) { result = getAnIndirectBarrierNode(_, p) }
/**
* Gets an indirect expression node that is safely guarded by the given guard check.
*
* See `getAnIndirectBarrierNode/1` for examples.
*/
Node getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) }
bindingset[value, n]
@@ -2575,10 +2542,10 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
}
private predicate guardChecksIndirectNode(
IRGuardCondition g, Node n, boolean branch, int indirectionIndex, P p
IRGuardCondition g, Node n, boolean branch, int indirectionIndex
) {
guardChecks(g, n.asIndirectOperand(indirectionIndex).getDef().getConvertedResultExpression(),
branch, p)
branch)
}
/**
@@ -2615,44 +2582,19 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
*
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
*/
Node getAnIndirectBarrierNode(int indirectionIndex, P p) {
Node getAnIndirectBarrierNode(int indirectionIndex) {
exists(IRGuardCondition g, ValueNumber value, boolean edge |
indirectConvertedExprHasValueNumber(indirectionIndex, value, result) and
guardChecks(g,
pragma[only_bind_into](value.getAnInstruction().getConvertedResultExpression()), edge, p) and
pragma[only_bind_into](value.getAnInstruction().getConvertedResultExpression()), edge) and
controls(g, result, edge)
)
or
result =
SsaImpl::BarrierGuardWithIntParam<P, guardChecksIndirectNode/5>::getABarrierNode(indirectionIndex,
p)
SsaImpl::BarrierGuardWithIntParam<guardChecksIndirectNode/4>::getABarrierNode(indirectionIndex)
}
}
/**
* Provides a set of barrier nodes for a guard that validates an expression.
*
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
* in data flow and taint tracking.
*/
module BarrierGuard<guardChecksSig/3 guardChecks> {
private predicate guardChecks(IRGuardCondition g, Expr e, boolean branch, Unit unit) {
guardChecks(g, e, branch) and
exists(unit)
}
import ParameterizedBarrierGuard<Unit, guardChecks/4>
}
private module InstrWithParam<ParamSig P> {
/**
* Holds if the guard `g` validates the instruction `instr` upon evaluating to `branch`.
*/
signature predicate instructionGuardChecksSig(
IRGuardCondition g, Instruction instr, boolean branch, P p
);
}
/**
* Holds if the guard `g` validates the instruction `instr` upon evaluating to `branch`.
*/
@@ -2664,9 +2606,7 @@ signature predicate instructionGuardChecksSig(IRGuardCondition g, Instruction in
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
* in data flow and taint tracking.
*/
module ParameterizedInstructionBarrierGuard<
ParamSig P, InstrWithParam<P>::instructionGuardChecksSig/4 instructionGuardChecks>
{
module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardChecks> {
bindingset[value, n]
pragma[inline_late]
private predicate operandHasValueNumber(ValueNumber value, Node n) {
@@ -2676,27 +2616,21 @@ module ParameterizedInstructionBarrierGuard<
)
}
private predicate guardChecksNode(IRGuardCondition g, Node n, boolean branch, P p) {
instructionGuardChecks(g, n.asOperand().getDef(), branch, p)
private predicate guardChecksNode(IRGuardCondition g, Node n, boolean branch) {
instructionGuardChecks(g, n.asOperand().getDef(), branch)
}
/**
* Gets a node that is safely guarded by the given guard check with
* parameter `p`.
*/
Node getABarrierNode(P p) {
/** Gets a node that is safely guarded by the given guard check. */
Node getABarrierNode() {
exists(IRGuardCondition g, ValueNumber value, boolean edge |
instructionGuardChecks(g, pragma[only_bind_into](value.getAnInstruction()), edge, p) and
instructionGuardChecks(g, pragma[only_bind_into](value.getAnInstruction()), edge) and
operandHasValueNumber(value, result) and
controls(g, result, edge)
)
or
result = SsaImpl::BarrierGuard<P, guardChecksNode/4>::getABarrierNode(p)
result = SsaImpl::BarrierGuard<guardChecksNode/3>::getABarrierNode()
}
/** Gets a node that is safely guarded by the given guard check. */
Node getABarrierNode() { result = getABarrierNode(_) }
bindingset[value, n]
pragma[inline_late]
private predicate indirectOperandHasValueNumber(ValueNumber value, int indirectionIndex, Node n) {
@@ -2707,52 +2641,25 @@ module ParameterizedInstructionBarrierGuard<
}
private predicate guardChecksIndirectNode(
IRGuardCondition g, Node n, boolean branch, int indirectionIndex, P p
IRGuardCondition g, Node n, boolean branch, int indirectionIndex
) {
instructionGuardChecks(g, n.asIndirectOperand(indirectionIndex).getDef(), branch, p)
instructionGuardChecks(g, n.asIndirectOperand(indirectionIndex).getDef(), branch)
}
/**
* Gets an indirect node with indirection index `indirectionIndex` that is
* safely guarded by the given guard check with parameter `p`.
* safely guarded by the given guard check.
*/
Node getAnIndirectBarrierNode(int indirectionIndex, P p) {
Node getAnIndirectBarrierNode(int indirectionIndex) {
exists(IRGuardCondition g, ValueNumber value, boolean edge |
instructionGuardChecks(g, pragma[only_bind_into](value.getAnInstruction()), edge, p) and
instructionGuardChecks(g, pragma[only_bind_into](value.getAnInstruction()), edge) and
indirectOperandHasValueNumber(value, indirectionIndex, result) and
controls(g, result, edge)
)
or
result =
SsaImpl::BarrierGuardWithIntParam<P, guardChecksIndirectNode/5>::getABarrierNode(indirectionIndex,
p)
SsaImpl::BarrierGuardWithIntParam<guardChecksIndirectNode/4>::getABarrierNode(indirectionIndex)
}
/**
* Gets an indirect node that is safely guarded by the given guard check
* with parameter `p`.
*/
Node getAnIndirectBarrierNode(P p) { result = getAnIndirectBarrierNode(_, p) }
/** Gets an indirect node that is safely guarded by the given guard check. */
Node getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) }
}
/**
* Provides a set of barrier nodes for a guard that validates an instruction.
*
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
* in data flow and taint tracking.
*/
module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardChecks> {
private predicate instructionGuardChecks(
IRGuardCondition g, Instruction i, boolean branch, Unit unit
) {
instructionGuardChecks(g, i, branch) and
exists(unit)
}
import ParameterizedInstructionBarrierGuard<Unit, instructionGuardChecks/4>
}
/**

View File

@@ -15,79 +15,17 @@ private import DataFlowPrivate
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() }
@@ -99,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
@@ -124,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
@@ -218,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
@@ -358,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
@@ -367,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() }
@@ -417,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
@@ -427,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
@@ -459,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 {
@@ -510,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) }
@@ -530,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;
@@ -564,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 }
@@ -593,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
@@ -685,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 }
}
/**
@@ -776,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() }
@@ -831,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() }
/**
@@ -877,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
}
@@ -1119,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;
@@ -1224,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
@@ -1251,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
@@ -1276,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
@@ -1298,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)
}
}
@@ -1362,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.

View File

@@ -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"
}

View File

@@ -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 }
}

View File

@@ -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`

View File

@@ -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() }

View File

@@ -57,4 +57,3 @@ private import implementations.CAtlFile
private import implementations.CAtlFileMapping
private import implementations.CAtlTemporaryFile
private import implementations.CRegKey
private import implementations.WinHttp

View File

@@ -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)
}
}

View File

@@ -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
)
}
}

View File

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

View File

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

View File

@@ -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.

View File

@@ -236,34 +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
);
/**
* 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(
string source_file: string ref,
int trap_file: @trap ref
);
/**
* Holds if there is a definition of `element` in TRAP file `trap_file`.
*/
in_trap(
int element: @element ref,
int trap_file: @trap ref
);
pch_uses(
int pch: @pch ref,
int compilation: @compilation ref,
@@ -2381,7 +2353,6 @@ case @preprocdirect.kind of
| 14 = @ppd_ms_import
| 15 = @ppd_elifdef
| 16 = @ppd_elifndef
| 17 = @ppd_embed
| 18 = @ppd_warning
;
@@ -2408,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
@@ -2423,8 +2389,6 @@ link_parent(
int link_target : @link_target ref
);
/*- Database metadata -*/
/**
* The CLI will automatically emit applicable tuples for this table,
* such as `databaseMetadata("isOverlay", "true")` when building an
@@ -2435,8 +2399,6 @@ databaseMetadata(
string value: string ref
);
/*- Overlay support -*/
/**
* The CLI will automatically emit tuples for each new/modified/deleted file
* when building an overlay database.

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
description: Sections for databaseMetadata and overlayChangedFiles
compatibility: full

View File

@@ -1,2 +0,0 @@
description: Support embed preprocessor directive
compatibility: partial

View File

@@ -1,2 +0,0 @@
description: Add trap_filename, source_file_uses_trap and in_trap relations
compatibility: full

View File

@@ -1,11 +1,3 @@
## 1.5.11
No user-facing changes.
## 1.5.10
No user-facing changes.
## 1.5.9
### Minor Analysis Improvements

View File

@@ -308,37 +308,3 @@ private module PossibleYearArithmeticOperationCheckConfig implements DataFlow::C
module PossibleYearArithmeticOperationCheckFlow =
TaintTracking::Global<PossibleYearArithmeticOperationCheckConfig>;
/**
* A time conversion function where either
* 1) an incorrect leap year date would result in an error that can be checked from the return value or
* 2) an incorrect leap year date is auto corrected (no checks required)
*/
class TimeConversionFunction extends Function {
boolean autoLeapYearCorrecting;
TimeConversionFunction() {
autoLeapYearCorrecting = false and
(
this.getName() =
[
"FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
"RtlTimeToSecondsSince1970", "_mkgmtime", "SetSystemTime", "VarUdateFromDate", "from_tm"
]
or
// Matches all forms of GetDateFormat, e.g. GetDateFormatA/W/Ex
this.getName().matches("GetDateFormat%")
)
or
autoLeapYearCorrecting = true and
this.getName() =
["mktime", "_mktime32", "_mktime64", "SystemTimeToVariantTime", "VariantTimeToSystemTime"]
}
/**
* Holds if the function is expected to auto convert a bad leap year date.
*/
predicate isAutoLeapYearCorrecting() { autoLeapYearCorrecting = true }
}

View File

@@ -1,7 +1,7 @@
/**
* @name Year field changed using an arithmetic operation without checking for leap year
* @description A field that represents a year is being modified by an arithmetic operation, but no proper check for leap years can be detected afterwards.
* @kind path-problem
* @kind problem
* @problem.severity warning
* @id cpp/leap-year/unchecked-after-arithmetic-year-modification
* @precision medium
@@ -11,844 +11,49 @@
import cpp
import LeapYear
import semmle.code.cpp.controlflow.IRGuards
/**
* Functions whose operations should never be considered a
* source or sink of a dangerous leap year operation.
* The general concept is to add conversion functions
* that convert one time type to another. Often
* other ignorable operation heuristics will filter these,
* but some cases, the simplest approach is to simply filter
* the function entirely.
* Note that flow through these functions should still be allowed
* we just cannot start or end flow from an operation to a
* year assignment in one of these functions.
*/
class IgnorableFunction extends Function {
IgnorableFunction() {
// arithmetic in known time conversion functions may look like dangerous operations
// we assume all known time conversion functions are safe.
this instanceof TimeConversionFunction
or
// Helper utility in postgres with string time conversions
this.getName() = "DecodeISO8601Interval"
or
// helper utility for date conversions in qtbase
this.getName() = "adjacentDay"
or
// Windows API function that does timezone conversions
this.getName().matches("%SystemTimeToTzSpecificLocalTime%")
or
// Windows APIs that do time conversions
this.getName().matches("%localtime%\\_s%")
or
// Windows APIs that do time conversions
this.getName().matches("%SpecificLocalTimeToSystemTime%")
or
// postgres function for diffing timestamps, date for leap year
// is not applicable.
this.getName().toLowerCase().matches("%timestamp%age%")
or
// Reading byte streams often involves operations of some base, but that's
// not a real source of leap year issues.
this.getName().toLowerCase().matches("%read%bytes%")
or
// A postgres function for local time conversions
// conversion operations (from one time structure to another) are generally ignorable
this.getName() = "localsub"
or
// Indication of a calendar not applicable to
// gregorian leap year, e.g., Hijri, Persian, Hebrew
this.getName().toLowerCase().matches("%hijri%")
or
this.getFile().getBaseName().toLowerCase().matches("%hijri%")
or
this.getName().toLowerCase().matches("%persian%")
or
this.getFile().getBaseName().toLowerCase().matches("%persian%")
or
this.getName().toLowerCase().matches("%hebrew%")
or
this.getFile().getBaseName().toLowerCase().matches("%hebrew%")
or
// misc. from string/char converters heuristic
this.getName()
.toLowerCase()
.matches(["%char%to%", "%string%to%", "%from%char%", "%from%string%"])
or
// boost's gregorian.cpp has year manipulations that are checked in complex ways.
// ignore the entire file as a source or sink.
this.getFile().getAbsolutePath().toLowerCase().matches("%boost%gregorian.cpp%")
}
}
/**
* The set of expressions which are ignorable; either because they seem to not be part of a year mutation,
* or because they seem to be a conversion pattern of mapping date scalars.
*/
abstract class IgnorableOperation extends Expr { }
class IgnorableExprRem extends IgnorableOperation instanceof RemExpr { }
/**
* An operation with 10, 100, 1000, 10000 as an operand is often a sign of conversion
* or atoi.
*/
class IgnorableExpr10MultipleComponent extends IgnorableOperation {
IgnorableExpr10MultipleComponent() {
this.(Operation).getAnOperand().getValue().toInt() in [10, 100, 1000, 10000]
or
exists(AssignOperation a | a.getRValue() = this |
a.getRValue().getValue().toInt() in [10, 100, 1000, 10000]
)
}
}
/**
* An operation involving a sub expression with char literal `48`, ignore as a likely string conversion. For example: `X - '0'`
*/
class IgnorableExpr48Mapping extends IgnorableOperation {
IgnorableExpr48Mapping() {
this.(SubExpr).getRightOperand().getValue().toInt() = 48
or
exists(AssignSubExpr e | e.getRValue() = this | e.getRValue().getValue().toInt() = 48)
}
}
/**
* A binary or arithmetic operation whereby one of the components is textual or a string.
*/
class IgnorableCharLiteralArithmetic extends IgnorableOperation {
IgnorableCharLiteralArithmetic() {
this.(BinaryArithmeticOperation).getAnOperand() instanceof TextLiteral
or
this instanceof TextLiteral and
any(AssignArithmeticOperation arith).getRValue() = this
}
}
/**
* Constants often used in date conversions (from one date data type to another)
* Numerous examples exist, like 1900 or 2000 that convert years from one
* representation to another.
* Also '0' is sometimes observed as an atoi style conversion.
*/
bindingset[c]
predicate isLikelyConversionConstant(int c) {
exists(int i | i = c.abs() |
i =
[
146097, // days in 400-year Gregorian cycle
36524, // days in 100-year Gregorian subcycle
1461, // days in 4-year cycle (incl. 1 leap)
32044, // Fliegel-van Flandern JDN epoch shift
1721425, // JDN of 0001-01-01 (Gregorian)
1721119, // alt epoch offset
2400000, // MJD -> JDN conversion
2400001, // alt MJD -> JDN conversion
2141, // fixed-point month/day extraction
65536, // observed in some conversions
7834, // observed in some conversions
256, // observed in some conversions
292275056, // qdatetime.h Qt Core year range first year constant
292278994, // qdatetime.h Qt Core year range last year constant
1601, // Windows FILETIME epoch start year
1970, // Unix epoch start year
70, // Unix epoch start year short form
1899, // Observed in uses with 1900 to address off by one scenarios
1900, // Used when converting a 2 digit year
2000, // Used when converting a 2 digit year
1400, // Hijri base year, used when converting a 2 digit year
1980, // FAT filesystem epoch start year
227013, // constant observed for Hirji year conversion, and Hirji years are not applicable for gregorian leap year
10631, // constant observed for Hirji year conversion, and Hirji years are not applicable for gregorian leap year,
80, // 1980/01/01 is the start of the epoch on DOS
0
]
)
}
/**
* An `isLikelyConversionConstant` constant indicates conversion that is ignorable, e.g.,
* julian to gregorian conversion or conversions from linux time structs
* that start at 1900, etc.
*/
class IgnorableConstantArithmetic extends IgnorableOperation {
IgnorableConstantArithmetic() {
exists(int i | isLikelyConversionConstant(i) |
this.(Operation).getAnOperand().getValue().toInt() = i
or
exists(AssignArithmeticOperation a | this = a.getRValue() |
a.getRValue().getValue().toInt() = i
)
)
}
}
// If a unary minus assume it is some sort of conversion
class IgnorableUnaryMinus extends IgnorableOperation {
IgnorableUnaryMinus() {
this instanceof UnaryMinusExpr
or
this.(Operation).getAnOperand() instanceof UnaryMinusExpr
}
}
/**
* An argument to a function is ignorable if the function that is called is an ignored function
*/
class OperationAsArgToIgnorableFunction extends IgnorableOperation {
OperationAsArgToIgnorableFunction() {
exists(Call c |
c.getAnArgument().getAChild*() = this and
c.getTarget() instanceof IgnorableFunction
)
}
}
/**
* A binary operation on two literals means the result is constant/known
* and the operation is basically ignorable (it's not a real operation but
* probably one visual simplicity what it means).
*/
class ConstantBinaryArithmeticOperation extends IgnorableOperation, BinaryArithmeticOperation {
ConstantBinaryArithmeticOperation() {
this.getLeftOperand() instanceof Literal and
this.getRightOperand() instanceof Literal
}
}
class IgnorableBinaryBitwiseOperation extends IgnorableOperation instanceof BinaryBitwiseOperation {
}
class IgnorableUnaryBitwiseOperation extends IgnorableOperation instanceof UnaryBitwiseOperation { }
class IgnorableAssignmentBitwiseOperation extends IgnorableOperation instanceof AssignBitwiseOperation
{ }
/**
* An arithmetic operation where one of the operands is a pointer or char type, ignore it
*/
class IgnorablePointerOrCharArithmetic extends IgnorableOperation {
IgnorablePointerOrCharArithmetic() {
this instanceof BinaryArithmeticOperation and
exists(Expr op | op = this.(BinaryArithmeticOperation).getAnOperand() |
op.getUnspecifiedType() instanceof PointerType
or
op.getUnspecifiedType() instanceof CharType
or
// Operations on calls to functions that accept char or char*
op.(Call).getAnArgument().getUnspecifiedType().stripType() instanceof CharType
or
// Operations on calls to functions named like "strlen", "wcslen", etc
// NOTE: workaround for cases where the wchar_t type is not a char, but an unsigned short
// unclear if there is a best way to filter cases like these out based on type info.
op.(Call).getTarget().getName().matches("%len%")
)
or
exists(AssignArithmeticOperation a | a.getRValue() = this |
exists(Expr op | op = a.getAnOperand() |
op.getUnspecifiedType() instanceof PointerType
or
op.getUnspecifiedType() instanceof CharType
or
// Operations on calls to functions that accept char or char*
op.(Call).getAnArgument().getUnspecifiedType().stripType() instanceof CharType
)
or
// Operations on calls to functions named like "strlen", "wcslen", etc
// for example `strlen(foo) + bar`
this.(BinaryArithmeticOperation).getAnOperand().(Call).getTarget().getName().matches("%len%")
)
}
}
/**
* Holds for an expression that is an add or similar operation that could flow to a Year field.
*/
predicate isOperationSourceCandidate(Expr e) {
not e instanceof IgnorableOperation and
exists(Function f |
f = e.getEnclosingFunction() and
not f instanceof IgnorableFunction
) and
(
e instanceof SubExpr
or
e instanceof AddExpr
or
e instanceof CrementOperation
or
e instanceof AssignSubExpr
or
e instanceof AssignAddExpr
)
}
/**
* A data flow that tracks an ignorable operation (such as a bitwise operation) to an operation source, so we may disqualify it.
*/
module IgnorableOperationToOperationSourceCandidateConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof IgnorableOperation }
predicate isSink(DataFlow::Node n) { isOperationSourceCandidate(n.asExpr()) }
// looking for sources and sinks in the same function
DataFlow::FlowFeature getAFeature() {
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
}
}
module IgnorableOperationToOperationSourceCandidateFlow =
TaintTracking::Global<IgnorableOperationToOperationSourceCandidateConfig>;
/**
* The set of all expressions which is a candidate expression and also does not flow from to to some ignorable expression (eg. bitwise op)
* ```
* a = something <<< 2;
* myDate.year = a + 1; // invalid
* ...
* a = someDate.year + 1;
* myDate.year = a; // valid
* ```
*/
class OperationSource extends Expr {
OperationSource() {
isOperationSourceCandidate(this) and
// If the candidate came from an ignorable operation, ignore the candidate
// NOTE: we cannot easily flow the candidate to an ignorable operation as that can
// be tricky in practice, e.g., a mod operation on a year would be part of a leap year check
// but a mod operation ending in a year is more indicative of something to ignore (a conversion)
not exists(IgnorableOperationToOperationSourceCandidateFlow::PathNode sink |
sink.getNode().asExpr() = this and
sink.isSink()
)
}
}
class YearFieldAssignmentNode extends DataFlow::Node {
YearFieldAccess access;
YearFieldAssignmentNode() {
exists(Function f |
f = this.getEnclosingCallable().getUnderlyingCallable() and not f instanceof IgnorableFunction
) and
(
this.asDefinition().(Assignment).getLValue() = access
or
this.asDefinition().(CrementOperation).getOperand() = access
or
exists(Call c | c.getAnArgument() = access and this.asDefiningArgument() = access)
or
exists(Call c, AddressOfExpr aoe |
c.getAnArgument() = aoe and
aoe.getOperand() = access and
this.asDefiningArgument() = aoe
)
)
}
YearFieldAccess getYearFieldAccess() { result = access }
}
/**
* A DataFlow configuration for identifying flows from an identified source
* to the Year field of a date object.
*/
module OperationToYearAssignmentConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof OperationSource }
predicate isSink(DataFlow::Node n) {
n instanceof YearFieldAssignmentNode and
not isYearModifiedWithCheck(n) and
not isControlledByMonthEqualityCheckNonFebruary(n.asExpr())
}
predicate isBarrier(DataFlow::Node n) {
exists(ArrayExpr arr | arr.getArrayOffset() = n.asExpr())
or
n.getType().getUnspecifiedType() instanceof PointerType
or
n.getType().getUnspecifiedType() instanceof CharType
or
// If a type resembles "string" ignore flow (likely string conversion, currently ignored)
n.getType().getUnspecifiedType().stripType().getName().toLowerCase().matches("%string%")
or
n.asExpr() instanceof IgnorableOperation
or
// Flowing into variables that indicate likely non-gregorian years are barriers
// e.g., names similar to hijri, persian, lunar, chinese, hebrew, etc.
exists(Variable v |
v.getName()
.toLowerCase()
.matches(["%hijri%", "%persian%", "%lunar%", "%chinese%", "%hebrew%"]) and
v.getAnAccess() = [n.asIndirectExpr(), n.asExpr()]
)
or
isLeapYearCheckSink(n)
or
// this is a bit of a hack to address cases where a year is normalized and checked, but the
// normalized year is never itself assigned to the final year struct
// isLeapYear(getCivilYear(year))
// struct.year = year
// This is assuming a user would have done this all on one line though.
// setting a variable for the conversion and passing that separately would be more difficult to track
// considering this approach good enough for current observed false positives
exists(Expr arg |
isLeapYearCheckCall(_, arg) and arg.getAChild*() = [n.asExpr(), n.asIndirectExpr()]
)
or
// If as the flow progresses, the value holding a dangerous operation result
// is apparently being passed by address to some function, it is more than likely
// intended to be modified, and therefore, the definition is killed.
exists(Call c | c.getAnArgument().(AddressOfExpr).getAnOperand() = n.asIndirectExpr())
}
/** Block flow out of an operation source to get the "closest" operation to the sink */
predicate isBarrierIn(DataFlow::Node n) { isSource(n) }
predicate isBarrierOut(DataFlow::Node n) { isSink(n) }
}
module OperationToYearAssignmentFlow = TaintTracking::Global<OperationToYearAssignmentConfig>;
predicate isLeapYearCheckSink(DataFlow::Node sink) {
exists(LeapYearGuardCondition lgc |
lgc.checkedYearAccess() = [sink.asExpr(), sink.asIndirectExpr()]
)
or
isLeapYearCheckCall(_, [sink.asExpr(), sink.asIndirectExpr()])
}
predicate yearAssignmentToCheckCommonSteps(DataFlow::Node node1, DataFlow::Node node2) {
// flow from a YearFieldAccess to the qualifier
node2.asExpr() = node1.asExpr().(YearFieldAccess).getQualifier*()
or
// getting the 'access' can be tricky at definitions (assignments especially)
// as dataflow uses asDefinition not asExpr.
// the YearFieldAssignmentNode holds the access in these cases
node1.(YearFieldAssignmentNode).getYearFieldAccess().getQualifier() = node2.asExpr()
or
// flow from a year access qualifier to a year field
exists(YearFieldAccess yfa | node2.asExpr() = yfa and node1.asExpr() = yfa.getQualifier())
or
node1.(YearFieldAssignmentNode).getYearFieldAccess().getQualifier() = node2.asExpr()
or
// Pass through any intermediate struct
exists(Assignment a |
a.getRValue() = node1.asExpr() and
node2.asExpr() = a.getLValue().(YearFieldAccess).getQualifier*()
)
or
// in cases of t.year = x and the value of x is checked, but the year t.year isn't directly checked
// flow from a year assignment node to an RHS if it is an assignment
// e.g.,
// t.year = x;
// isLeapYear(x);
// --> at this point there is no flow of t.year to a check, but only its raw value
// To detect the flow of 'x' to the isLeapYear check,
// flow from t.year to 'x' (at assignment, t.year = x, flow to the RHS to track use-use flow of x)
exists(YearFieldAssignmentNode yfan |
node1 = yfan and
node2.asExpr() = yfan.asDefinition().(Assignment).getRValue()
)
}
/**
* A flow configuration from a Year field access to some Leap year check or guard
*/
module YearAssignmentToLeapYearCheckConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof YearFieldAssignmentNode }
predicate isSink(DataFlow::Node sink) { isLeapYearCheckSink(sink) }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
yearAssignmentToCheckCommonSteps(node1, node2)
}
/**
* Enforcing the check must occur in the same call context as the source,
* i.e., do not return from the source function and check in a caller.
*/
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
}
module YearAssignmentToLeapYearCheckFlow =
TaintTracking::Global<YearAssignmentToLeapYearCheckConfig>;
/** Does there exist a flow from the given YearFieldAccess to a Leap Year check or guard? */
predicate isYearModifiedWithCheck(YearFieldAssignmentNode n) {
exists(YearAssignmentToLeapYearCheckFlow::PathNode src |
src.isSource() and
src.getNode() = n
)
or
// If the time flows to a time conversion whose value/result is checked,
// assume the leap year is being handled.
exists(YearAssignmentToCheckedTimeConversionFlow::PathNode timeQualSrc |
timeQualSrc.isSource() and
timeQualSrc.getNode() = n
)
}
/**
* An expression which checks the value of a Month field `a->month == 1`.
*/
class MonthEqualityCheck extends EqualityOperation {
MonthEqualityCheck() { this.getAnOperand() instanceof MonthFieldAccess }
Expr getExprCompared() {
exists(Expr e |
e = this.getAnOperand() and
not e instanceof MonthFieldAccess and
result = e
)
}
}
final class FinalMonthEqualityCheck = MonthEqualityCheck;
class MonthEqualityCheckGuard extends GuardCondition, FinalMonthEqualityCheck { }
/**
* Verifies if the expression is guarded by a check on the Month property of a date struct, that is NOT February.
*/
bindingset[e]
pragma[inline_late]
predicate isControlledByMonthEqualityCheckNonFebruary(Expr e) {
exists(MonthEqualityCheckGuard monthGuard, Expr compared |
monthGuard.controls(e.getBasicBlock(), true) and
compared = monthGuard.getExprCompared() and
not compared.getValue().toInt() = 2
)
}
/**
* Flow from a year field access to a time conversion function
* that auto converts feb29 in non-leap year, or through a conversion function that doesn't
* auto convert to a sanity check guard of the result for error conditions.
*/
module YearAssignmentToCheckedTimeConversionConfig implements DataFlow::StateConfigSig {
// Flow state tracks if flow goes through a known time conversion function
// see `TimeConversionFunction`.
// A valid check with a time conversion function is either the case:
// 1) the year flows into a time conversion function, and the time conversion function's result is checked or
// 2) the year flows into a time conversion function that auto corrects for leap year, so no check is necessary.
class FlowState = boolean;
predicate isSource(DataFlow::Node source, FlowState state) {
source instanceof YearFieldAssignmentNode and
state = false
}
predicate isSink(DataFlow::Node sink, FlowState state) {
// Case 1: Flow through a time conversion function that requires a check,
// and we have arrived at a guard, implying the result was checked for possible error, including leap year error.
// state = true indicates the flow went through a time conversion function
state = true and
(
exists(IfStmt ifs | ifs.getCondition().getAChild*() = [sink.asExpr(), sink.asIndirectExpr()])
or
exists(ConditionalExpr ce |
ce.getCondition().getAChild*() = [sink.asExpr(), sink.asIndirectExpr()]
)
or
exists(Loop l | l.getCondition().getAChild*() = [sink.asExpr(), sink.asIndirectExpr()])
)
or
// Case 2: Flow through a time conversion function that auto corrects for leap year, so no check is necessary.
// state true or false, as flowing through a time conversion function is not necessary in this instance.
state in [true, false] and
exists(Call c, TimeConversionFunction f |
f.isAutoLeapYearCorrecting() and
c.getTarget() = f and
c.getAnArgument().getAChild*() = [sink.asExpr(), sink.asIndirectExpr()]
)
}
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
state1 in [true, false] and
state2 = true and
exists(Call c |
c.getTarget() instanceof TimeConversionFunction and
c.getAnArgument().getAChild*() = [node1.asExpr(), node1.asIndirectExpr()] and
node2.asExpr() = c
)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
yearAssignmentToCheckCommonSteps(node1, node2)
}
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
}
module YearAssignmentToCheckedTimeConversionFlow =
DataFlow::GlobalWithState<YearAssignmentToCheckedTimeConversionConfig>;
/**
* Finds flow from a parameter of a function to a leap year check.
* This is necessary to handle for scenarios like this:
*
* year = DANGEROUS_OP // source
* isLeap = isLeapYear(year);
* // logic based on isLeap
* struct.year = year; // sink
*
* In this case, we may flow a dangerous op to a year assignment, failing
* to barrier the flow through a leap year check, as the leap year check
* is nested, and dataflow does not progress down into the check and out.
* Instead, the point of this flow is to detect isLeapYear's argument
* is checked for leap year, making the isLeapYear call a barrier for
* the dangerous flow if we flow through the parameter identified to
* be checked.
*/
module ParameterToLeapYearCheckConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { exists(source.asParameter()) }
predicate isSink(DataFlow::Node sink) {
exists(LeapYearGuardCondition lgc |
lgc.checkedYearAccess() = [sink.asExpr(), sink.asIndirectExpr()]
)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
// flow from a YearFieldAccess to the qualifier
node2.asExpr() = node1.asExpr().(YearFieldAccess).getQualifier*()
or
// flow from a year access qualifier to a year field
exists(YearFieldAccess yfa | node2.asExpr() = yfa and node1.asExpr() = yfa.getQualifier())
}
/**
* Enforcing the check must occur in the same call context as the source,
* i.e., do not return from the source function and check in a caller.
*/
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
}
// NOTE: I do not believe taint flow is necessary here as we should
// be flowing directyly from some parameter to a leap year check.
module ParameterToLeapYearCheckFlow = DataFlow::Global<ParameterToLeapYearCheckConfig>;
predicate isLeapYearCheckCall(Call c, Expr arg) {
exists(ParameterToLeapYearCheckFlow::PathNode src, Function f, int i |
src.isSource() and
f.getParameter(i) = src.getNode().asParameter() and
c.getTarget() = f and
c.getArgument(i) = arg
)
}
class LeapYearGuardCondition extends GuardCondition {
Expr yearSinkDiv4;
Expr yearSinkDiv100;
Expr yearSinkDiv400;
LeapYearGuardCondition() {
exists(
LogicalAndExpr andExpr, LogicalOrExpr orExpr, GuardCondition div4Check,
GuardCondition div100Check, GuardCondition div400Check, GuardValue gv
|
// canonical case:
// form: `(year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)`
// `!((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0))`
// `!(year % 4) && (year % 100 || !(year % 400))`
// Also accepting `((year & 3) == 0) && (year % 100 != 0 || year % 400 == 0)`
// and `(year % 4 == 0) && (year % 100 > 0 || year % 400 == 0)`
this = andExpr and
andExpr.hasOperands(div4Check, orExpr) and
orExpr.hasOperands(div100Check, div400Check) and
(
// year % 4 == 0
exists(RemExpr e |
div4Check.comparesEq(e, 0, true, gv) and
e.getRightOperand().getValue().toInt() = 4 and
yearSinkDiv4 = e.getLeftOperand()
)
or
// year & 3 == 0
exists(BitwiseAndExpr e |
div4Check.comparesEq(e, 0, true, gv) and
e.getRightOperand().getValue().toInt() = 3 and
yearSinkDiv4 = e.getLeftOperand()
)
) and
exists(RemExpr e |
// year % 100 != 0 or year % 100 > 0
(
div100Check.comparesEq(e, 0, false, gv) or
div100Check.comparesLt(e, 1, false, gv)
) and
e.getRightOperand().getValue().toInt() = 100 and
yearSinkDiv100 = e.getLeftOperand()
) and
// year % 400 == 0
exists(RemExpr e |
div400Check.comparesEq(e, 0, true, gv) and
e.getRightOperand().getValue().toInt() = 400 and
yearSinkDiv400 = e.getLeftOperand()
)
or
// Inverted logic case:
// `year % 4 != 0 || (year % 100 == 0 && year % 400 != 0)`
// or `year & 3 != 0 || (year % 100 == 0 && year % 400 != 0)`
// also accepting `year % 4 > 0 || (year % 100 == 0 && year % 400 > 0)`
this = orExpr and
orExpr.hasOperands(div4Check, andExpr) and
andExpr.hasOperands(div100Check, div400Check) and
(
// year % 4 != 0 or year % 4 > 0
exists(RemExpr e |
(
div4Check.comparesEq(e, 0, false, gv)
or
div4Check.comparesLt(e, 1, false, gv)
) and
e.getRightOperand().getValue().toInt() = 4 and
yearSinkDiv4 = e.getLeftOperand()
)
or
// year & 3 != 0
exists(BitwiseAndExpr e |
div4Check.comparesEq(e, 0, false, gv) and
e.getRightOperand().getValue().toInt() = 3 and
yearSinkDiv4 = e.getLeftOperand()
)
) and
// year % 100 == 0
exists(RemExpr e |
div100Check.comparesEq(e, 0, true, gv) and
e.getRightOperand().getValue().toInt() = 100 and
yearSinkDiv100 = e.getLeftOperand()
) and
// year % 400 != 0 or year % 400 > 0
exists(RemExpr e |
(
div400Check.comparesEq(e, 0, false, gv)
or
div400Check.comparesLt(e, 1, false, gv)
) and
e.getRightOperand().getValue().toInt() = 400 and
yearSinkDiv400 = e.getLeftOperand()
)
)
}
Expr getYearSinkDiv4() { result = yearSinkDiv4 }
Expr getYearSinkDiv100() { result = yearSinkDiv100 }
Expr getYearSinkDiv400() { result = yearSinkDiv400 }
/**
* Gets the variable access that is used in all 3 components of the leap year check
* e.g., see getYearSinkDiv4/100/400..
* If a field access is used, the qualifier and the field access are both returned
* in checked condition.
* NOTE: if the year is not checked using the same access in all 3 components, no result is returned.
* The typical case observed is a consistent variable access is used. If not, this may indicate a bug.
* We could check more accurately with a dataflow analysis, but this is likely sufficient for now.
*/
VariableAccess checkedYearAccess() {
exists(Variable var |
(
this.getYearSinkDiv4().getAChild*() = var.getAnAccess() and
this.getYearSinkDiv100().getAChild*() = var.getAnAccess() and
this.getYearSinkDiv400().getAChild*() = var.getAnAccess() and
result = var.getAnAccess() and
(
result = this.getYearSinkDiv4().getAChild*() or
result = this.getYearSinkDiv100().getAChild*() or
result = this.getYearSinkDiv400().getAChild*()
)
)
)
}
}
/**
* A difficult case to detect is if a year modification is tied to a month or day modification
* and the month or day is safe for leap year.
* e.g.,
* year++;
* month = 1;
* // alternative: day = 15;
* ... values eventually used in the same time struct
* If this is even more challenging if the struct the values end up in are not
* local (set inter-procedurally).
* This configuration looks for constants 1-31 flowing to a month or day assignment.
* It is assumed a user of this flow will check if the month/day source and month/day sink
* are in the same basic blocks as a year modification source and a year modification sink.
* It is also assumed a user will check if the constant source is a value that is ignorable
* e.g., if it is 2 and the sink is a month assignment, then it isn't ignorable or
* if the value is < 27 and is a day assignment, it is likely ignorable
*
* Obviously this does not handle all conditions (e.g., the month set in another block).
* It is meant to capture the most common cases of false positives.
*/
module CandidateConstantToDayOrMonthAssignmentConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source.asExpr().getValue().toInt() in [1 .. 31] and
(
exists(Assignment a | a.getRValue() = source.asExpr())
or
exists(Call c | c.getAnArgument() = source.asExpr())
)
}
predicate isSink(DataFlow::Node sink) {
exists(Assignment a |
(a.getLValue() instanceof MonthFieldAccess or a.getLValue() instanceof DayFieldAccess) and
a.getRValue() = sink.asExpr()
)
}
}
// NOTE: only data flow here (no taint tracking) as we want the exact
// constant flowing to the month assignment
module CandidateConstantToDayOrMonthAssignmentFlow =
DataFlow::Global<CandidateConstantToDayOrMonthAssignmentConfig>;
/**
* Holds if value the assignment `a` resolves to (`dayOrMonthValSrcExpr`) doesn't represent February,
* and/or if it represents a day, is a 'safe' day (meaning the 27th or prior).
*/
bindingset[dayOrMonthValSrcExpr]
predicate isSafeValueForAssignmentOfMonthOrDayValue(Assignment a, Expr dayOrMonthValSrcExpr) {
a.getLValue() instanceof MonthFieldAccess and
dayOrMonthValSrcExpr.getValue().toInt() != 2
or
a.getLValue() instanceof DayFieldAccess and
dayOrMonthValSrcExpr.getValue().toInt() <= 27
}
import OperationToYearAssignmentFlow::PathGraph
from OperationToYearAssignmentFlow::PathNode src, OperationToYearAssignmentFlow::PathNode sink
from Variable var, LeapYearFieldAccess yfa
where
OperationToYearAssignmentFlow::flowPath(src, sink) and
// Check if a month is set in the same block as the year operation source
// and the month value would indicate its set to any other month than february.
// Finds if the source year node is in the same block as a source month block
// and if the same for the sinks.
not exists(DataFlow::Node dayOrMonthValSrc, DataFlow::Node dayOrMonthValSink, Assignment a |
CandidateConstantToDayOrMonthAssignmentFlow::flow(dayOrMonthValSrc, dayOrMonthValSink) and
a.getRValue() = dayOrMonthValSink.asExpr() and
dayOrMonthValSink.getBasicBlock() = sink.getNode().getBasicBlock() and
exists(IRBlock dayOrMonthValBB |
dayOrMonthValBB = dayOrMonthValSrc.getBasicBlock() and
// The source of the day is set in the same block as the source for the year
// or the source for the day is set in the same block as the sink for the year
dayOrMonthValBB in [
src.getNode().getBasicBlock(),
sink.getNode().getBasicBlock()
]
) and
isSafeValueForAssignmentOfMonthOrDayValue(a, dayOrMonthValSrc.asExpr())
exists(VariableAccess va |
yfa.getQualifier() = va and
var.getAnAccess() = va and
// The year is modified with an arithmetic operation. Avoid values that are likely false positives
yfa.isModifiedByArithmeticOperationNotForNormalization() and
// Avoid false positives
not (
// If there is a local check for leap year after the modification
exists(LeapYearFieldAccess yfacheck |
yfacheck.getQualifier() = var.getAnAccess() and
yfacheck.isUsedInCorrectLeapYearCheck() and
yfacheck.getBasicBlock() = yfa.getBasicBlock().getASuccessor*()
)
or
// If there is a data flow from the variable that was modified to a function that seems to check for leap year
exists(VariableAccess source, ChecksForLeapYearFunctionCall fc |
source = var.getAnAccess() and
LeapYearCheckFlow::flow(DataFlow::exprNode(source), DataFlow::exprNode(fc.getAnArgument()))
)
or
// If there is a data flow from the field that was modified to a function that seems to check for leap year
exists(VariableAccess vacheck, YearFieldAccess yfacheck, ChecksForLeapYearFunctionCall fc |
vacheck = var.getAnAccess() and
yfacheck.getQualifier() = vacheck and
LeapYearCheckFlow::flow(DataFlow::exprNode(yfacheck), DataFlow::exprNode(fc.getAnArgument()))
)
or
// If there is a successor or predecessor that sets the month = 1
exists(MonthFieldAccess mfa, AssignExpr ae |
mfa.getQualifier() = var.getAnAccess() and
mfa.isModified() and
(
mfa.getBasicBlock() = yfa.getBasicBlock().getASuccessor*() or
yfa.getBasicBlock() = mfa.getBasicBlock().getASuccessor+()
) and
ae = mfa.getEnclosingElement() and
ae.getAnOperand().getValue().toInt() = 1
)
)
)
select sink, src, sink,
"Year field has been modified, but no appropriate check for LeapYear was found."
select yfa,
"Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found.",
yfa.getTarget(), yfa.getTarget().toString(), var, var.toString()

View File

@@ -44,9 +44,23 @@ class SafeTimeGatheringFunction extends Function {
}
}
/**
* This list of APIs should check for the return value to detect problems during the conversion.
*/
class TimeConversionFunction extends Function {
TimeConversionFunction() {
this.getQualifiedName() =
[
"FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
"RtlTimeToSecondsSince1970", "_mkgmtime"
]
}
}
from FunctionCall fcall, TimeConversionFunction trf, Variable var
where
not trf.isAutoLeapYearCorrecting() and
fcall = trf.getACallToThisFunction() and
fcall instanceof ExprInVoidContext and
var.getUnderlyingType() instanceof UnpackedTimeType and

View File

@@ -16,15 +16,17 @@
import cpp
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow
import ReturnStackAllocatedMemory::PathGraph
import PathGraph
/** Holds if `f` has a name that we interpret as evidence of intentionally returning the value of the stack pointer. */
predicate intentionallyReturnsStackPointer(Function f) {
f.getName().toLowerCase().matches(["%stack%", "%sp%"])
}
module ReturnStackAllocatedMemoryConfig implements MustFlow::ConfigSig {
predicate isSource(Instruction source) {
class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
ReturnStackAllocatedMemoryConfig() { this = "ReturnStackAllocatedMemoryConfig" }
override predicate isSource(Instruction source) {
exists(Function func |
// Rule out FPs caused by extraction errors.
not func.hasErrors() and
@@ -48,7 +50,7 @@ module ReturnStackAllocatedMemoryConfig implements MustFlow::ConfigSig {
)
}
predicate isSink(Operand sink) {
override predicate isSink(Operand sink) {
// Holds if `sink` is a node that represents the `StoreInstruction` that is subsequently used in
// a `ReturnValueInstruction`.
// We use the `StoreInstruction` instead of the instruction that defines the
@@ -70,7 +72,7 @@ module ReturnStackAllocatedMemoryConfig implements MustFlow::ConfigSig {
// int* px = id(&x);
// }
// ```
predicate allowInterproceduralFlow() { none() }
override predicate allowInterproceduralFlow() { none() }
/**
* This configuration intentionally conflates addresses of fields and their object, and pointer offsets
@@ -85,22 +87,20 @@ module ReturnStackAllocatedMemoryConfig implements MustFlow::ConfigSig {
* }
* ```
*/
predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
override predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
node2.(FieldAddressInstruction).getObjectAddressOperand() = node1
or
node2.(PointerOffsetInstruction).getLeftOperand() = node1
}
predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType }
override predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType }
}
module ReturnStackAllocatedMemory = MustFlow::Global<ReturnStackAllocatedMemoryConfig>;
from
ReturnStackAllocatedMemory::PathNode source, ReturnStackAllocatedMemory::PathNode sink,
Instruction instr
MustFlowPathNode source, MustFlowPathNode sink, Instruction instr,
ReturnStackAllocatedMemoryConfig conf
where
ReturnStackAllocatedMemory::flowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
source.getInstruction() = instr
select sink.getInstruction(), source, sink, "May return stack-allocated memory from $@.",
instr.getAst(), instr.getAst().toString()

View File

@@ -15,7 +15,7 @@
import cpp
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow
import UninitializedLocal::PathGraph
import PathGraph
/**
* Auxiliary predicate: Types that don't require initialization
@@ -70,26 +70,25 @@ predicate isSinkImpl(Instruction sink, VariableAccess va) {
)
}
module UninitializedLocalConfig implements MustFlow::ConfigSig {
predicate isSource(Instruction source) {
class MustFlow extends MustFlowConfiguration {
MustFlow() { this = "MustFlow" }
override predicate isSource(Instruction source) {
source instanceof UninitializedInstruction and
exists(Type t | t = source.getResultType() | not allocatedType(t))
}
predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
override predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
predicate allowInterproceduralFlow() { none() }
override predicate allowInterproceduralFlow() { none() }
predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
override predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
}
module UninitializedLocal = MustFlow::Global<UninitializedLocalConfig>;
from
VariableAccess va, LocalVariable v, UninitializedLocal::PathNode source,
UninitializedLocal::PathNode sink
VariableAccess va, LocalVariable v, MustFlow conf, MustFlowPathNode source, MustFlowPathNode sink
where
UninitializedLocal::flowPath(source, sink) and
conf.hasFlowPath(source, sink) and
isSinkImpl(sink.getInstruction(), va) and
v = va.getTarget()
select va, source, sink, "The variable $@ may not be initialized at this access.", v, v.getName()

View File

@@ -17,16 +17,16 @@
import cpp
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow
import UnsafeUseOfThis::PathGraph
import PathGraph
module UnsafeUseOfThisConfig implements MustFlow::ConfigSig {
predicate isSource(Instruction source) { isSource(source, _, _) }
class UnsafeUseOfThisConfig extends MustFlowConfiguration {
UnsafeUseOfThisConfig() { this = "UnsafeUseOfThisConfig" }
predicate isSink(Operand sink) { isSink(sink, _) }
override predicate isSource(Instruction source) { isSource(source, _, _) }
override predicate isSink(Operand sink) { isSink(sink, _) }
}
module UnsafeUseOfThis = MustFlow::Global<UnsafeUseOfThisConfig>;
/** Holds if `sink` is a `this` pointer used by the call instruction `call`. */
predicate isSink(Operand sink, CallInstruction call) {
exists(PureVirtualFunction func |
@@ -66,17 +66,19 @@ predicate isSource(InitializeParameterInstruction source, string msg, Class c) {
* - `msg` is a string describing whether `source` is from a constructor or destructor.
*/
predicate flows(
UnsafeUseOfThis::PathNode source, string msg, Class sourceClass, UnsafeUseOfThis::PathNode sink,
MustFlowPathNode source, string msg, Class sourceClass, MustFlowPathNode sink,
CallInstruction call
) {
UnsafeUseOfThis::flowPath(source, sink) and
isSource(source.getInstruction(), msg, sourceClass) and
isSink(sink.getInstruction().getAUse(), call)
exists(UnsafeUseOfThisConfig conf |
conf.hasFlowPath(source, sink) and
isSource(source.getInstruction(), msg, sourceClass) and
isSink(sink.getInstruction().getAUse(), call)
)
}
from
UnsafeUseOfThis::PathNode source, UnsafeUseOfThis::PathNode sink, CallInstruction call,
string msg, Class sourceClass
MustFlowPathNode source, MustFlowPathNode sink, CallInstruction call, string msg,
Class sourceClass
where
flows(source, msg, sourceClass, sink, call) and
// Only raise an alert if there is no override of the pure virtual function in any base class.

View File

@@ -45,15 +45,14 @@ module SqlTaintedConfig implements DataFlow::ConfigSig {
predicate isBarrier(DataFlow::Node node) {
node.asExpr().getUnspecifiedType() instanceof IntegralType
or
}
predicate isBarrierIn(DataFlow::Node node) {
exists(SqlBarrierFunction sql, int arg, FunctionInput input |
node.asIndirectArgument() = sql.getACallToThisFunction().getArgument(arg) and
input.isParameterDeref(arg) and
sql.barrierSqlArgument(input, _)
)
or
// barrier defined using models-as-data
barrierNode(node, "sql-injection")
}
predicate observeDiffInformedIncrementalMode() { any() }

View File

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

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