Merge branch 'main' into mapfix

This commit is contained in:
Geoffrey White
2026-03-04 10:20:04 +00:00
committed by GitHub
829 changed files with 70632 additions and 32231 deletions

View File

@@ -11,6 +11,8 @@ 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.
@@ -34,7 +36,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_java,+@rules_shell"
common --incompatible_autoload_externally="+@rules_cc,+@rules_java,+@rules_shell"
build --java_language_version=17
build --tool_java_language_version=17

View File

@@ -1 +1 @@
8.4.2
9.0.0

View File

@@ -15,20 +15,22 @@ 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_go", version = "0.56.1")
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_pkg", version = "1.0.1")
bazel_dep(name = "rules_nodejs", version = "6.2.0-codeql.1")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
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.0-codeql.1")
bazel_dep(name = "gazelle", version = "0.40.0")
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
bazel_dep(name = "gazelle", version = "0.47.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.66.0")
bazel_dep(name = "rules_rust", version = "0.68.1.codeql.1")
bazel_dep(name = "zstd", version = "1.5.5.bcr.1")
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
@@ -41,7 +43,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/2025-08-01"
RUST_VERSION = "nightly/2026-01-22"
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
@@ -53,26 +55,26 @@ rust.toolchain(
],
# generated by buildutils-internal/scripts/fill-rust-sha256s.py (internal repo)
sha256s = {
"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",
"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",
},
versions = [RUST_VERSION],
)
@@ -188,6 +190,15 @@ 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`
@@ -254,11 +265,11 @@ use_repo(
)
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.25.0")
go_sdk.download(version = "1.26.0")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//go/extractor:go.mod")
use_repo(go_deps, "org_golang_x_mod", "org_golang_x_tools")
use_repo(go_deps, "com_github_stretchr_testify", "org_golang_x_mod", "org_golang_x_tools")
ripunzip_archive = use_repo_rule("//misc/ripunzip:ripunzip.bzl", "ripunzip_archive")

View File

@@ -1,3 +1,7 @@
## 0.4.28
No user-facing changes.
## 0.4.27
### Bug Fixes

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
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,3 +1,9 @@
## 7.1.1
### Minor Analysis Improvements
* Added remote flow source models for the `winhttp.h` windows header and the Azure SDK core library for C/C++.
## 7.1.0
### New Features

View File

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

View File

@@ -0,0 +1,4 @@
---
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

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

View File

@@ -0,0 +1,4 @@
---
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

@@ -0,0 +1,4 @@
---
category: breaking
---
* CodeQL version 2.24.2 accidentially introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.

View File

@@ -0,0 +1,5 @@
## 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.0
lastReleaseVersion: 7.1.1

View File

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

View File

@@ -14,7 +14,9 @@ class PackedTimeType extends Type {
}
}
private predicate timeType(string typeName) { typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm"] }
private predicate timeType(string typeName) {
typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm", "TIME_FIELDS", "_TIME_FIELDS", "PTIME_FIELDS"]
}
/**
* A type that is used to represent times and dates in an 'unpacked' form, that is,
@@ -95,3 +97,24 @@ 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

@@ -555,6 +555,7 @@ private Locatable getSupportedFunctionTemplateArgument(Function templateFunction
* Normalize the `n`'th parameter of `f` by replacing template names
* with `func:N` (where `N` is the index of the template).
*/
pragma[nomagic]
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
exists(Function templateFunction |
templateFunction = getFullyTemplatedFunction(f) and

View File

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

View File

@@ -34,6 +34,38 @@ 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)
)

View File

@@ -8,81 +8,143 @@ private import cpp
private import semmle.code.cpp.ir.IR
/**
* 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).
* Provides an inter-procedural must-flow data flow analysis.
*/
abstract class MustFlowConfiguration extends string {
bindingset[this]
MustFlowConfiguration() { any() }
module MustFlow {
/**
* Holds if `source` is a relevant data flow source.
* 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).
*/
abstract predicate isSource(Instruction source);
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.
*/
abstract predicate isSink(Operand sink);
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(Operand sink);
/**
* Holds if data flow through `instr` is prohibited.
*/
predicate isBarrier(Instruction instr) { none() }
/**
* 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.
*/
predicate isAdditionalFlowStep(Operand node1, Instruction node2) { 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. */
predicate allowInterproceduralFlow() { any() }
/**
* 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 this configuration allows flow from arguments to parameters. */
default predicate allowInterproceduralFlow() { any() }
}
}
/** 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))
)
)
}
/**
* Constructs a global must-flow computation.
*/
module Global<ConfigSig Config> {
import 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))
)
)
/**
* 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 `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 `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 `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)
}
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()
}
}
}
}
cached
@@ -102,7 +164,7 @@ private module Cached {
not f.isVirtual() and
call.getPositionalArgument(n) = instr and
f = call.getStaticCallTarget() and
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
isEnclosingNonVirtualFunctionInitializeParameter(init, f) and
init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n))
}
@@ -111,7 +173,7 @@ private module Cached {
* corresponding initialization instruction that receives the value of `instr` in `f`.
*/
pragma[noinline]
private predicate getPositionalArgumentInitParam(
private predicate isPositionalArgumentInitParam(
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
) {
exists(int n |
@@ -126,18 +188,18 @@ private module Cached {
* `instr` in `f`.
*/
pragma[noinline]
private predicate getThisArgumentInitParam(
private predicate isThisArgumentInitParam(
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
) {
not f.isVirtual() and
call.getStaticCallTarget() = f and
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
isEnclosingNonVirtualFunctionInitializeParameter(init, f) and
call.getThisArgument() = instr and
init.getIRVariable() instanceof IRThisVariable
}
/** Holds if `f` is the enclosing non-virtual function of `init`. */
private predicate getEnclosingNonVirtualFunctionInitializeParameter(
private predicate isEnclosingNonVirtualFunctionInitializeParameter(
InitializeParameterInstruction init, Function f
) {
not f.isVirtual() and
@@ -145,7 +207,7 @@ private module Cached {
}
/** Holds if `f` is the enclosing non-virtual function of `init`. */
private predicate getEnclosingNonVirtualFunctionInitializeIndirection(
private predicate isEnclosingNonVirtualFunctionInitializeIndirection(
InitializeIndirectionInstruction init, Function f
) {
not f.isVirtual() and
@@ -153,15 +215,16 @@ private module Cached {
}
/**
* Holds if `instr` is an argument (or argument indirection) to a call, and
* `succ` is the corresponding initialization instruction in the call target.
* Holds if `argument` is an argument (or argument indirection) to a call, and
* `parameter` is the corresponding initialization instruction in the call target.
*/
private predicate flowThroughCallable(Instruction argument, Instruction parameter) {
cached
predicate flowThroughCallable(Instruction argument, Instruction parameter) {
// Flow from an argument to a parameter
exists(CallInstruction call, InitializeParameterInstruction init | init = parameter |
getPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget())
isPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget())
or
getThisArgumentInitParam(call, argument, init, call.getStaticCallTarget())
isThisArgumentInitParam(call, argument, init, call.getStaticCallTarget())
)
or
// Flow from argument indirection to parameter indirection
@@ -170,7 +233,7 @@ private module Cached {
|
init = parameter and
read.getPrimaryInstruction() = call and
getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
isEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
exists(int n |
read.getSideEffectOperand().getAnyDef() = argument and
@@ -205,92 +268,10 @@ private module Cached {
}
cached
predicate step(Instruction nodeFrom, Instruction nodeTo) {
predicate localStep(Instruction nodeFrom, Instruction nodeTo) {
exists(Operand mid |
instructionToOperandStep(nodeFrom, mid) and
operandToInstructionStep(mid, nodeTo)
)
or
flowThroughCallable(nodeFrom, nodeTo)
}
}
/**
* Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this
* predicate ensures that joins go from `n` to the result instead of the other
* way around.
*/
pragma[inline]
private IRFunction getEnclosingCallable(Instruction n) {
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction()
}
/** Holds if `nodeFrom` flows to `nodeTo`. */
private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) {
exists(config) and
Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
(
config.allowInterproceduralFlow()
or
getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
)
or
config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
}
private newtype TLocalPathNode =
MkLocalPathNode(Instruction n, MustFlowConfiguration config) {
flowsToSink(n, config) and
(
config.isSource(n)
or
exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config))
)
}
/** A `Node` that is in a path from a source to a sink. */
class MustFlowPathNode extends TLocalPathNode {
Instruction n;
MustFlowPathNode() { this = MkLocalPathNode(n, _) }
/** Gets the underlying node. */
Instruction getInstruction() { result = n }
/** Gets a textual representation of this node. */
string toString() { result = n.getAst().toString() }
/** Gets the location of this element. */
Location getLocation() { result = n.getLocation() }
/** Gets a successor node, if any. */
MustFlowPathNode getASuccessor() {
step(this.getInstruction(), result.getInstruction(), this.getConfiguration())
}
/** Gets the associated configuration. */
MustFlowConfiguration getConfiguration() { this = MkLocalPathNode(_, result) }
}
private class MustFlowPathSink extends MustFlowPathNode {
MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) }
}
/**
* Provides the query predicates needed to include a graph in a path-problem query.
*/
module PathGraph {
private predicate reach(MustFlowPathNode n) {
n instanceof MustFlowPathSink or reach(n.getASuccessor())
}
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(MustFlowPathNode a, MustFlowPathNode b) {
a.getASuccessor() = b and reach(b)
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(MustFlowPathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,47 +4,12 @@ import semmle.code.cpp.ir.internal.IRCppLanguage
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
private import DataFlowImplCommon as DataFlowImplCommon
private import DataFlowUtil
private import DataFlowNodes
private import semmle.code.cpp.models.interfaces.PointerWrapper
private import DataFlowPrivate
private import TypeFlow
private import semmle.code.cpp.ir.ValueNumbering
/**
* Holds if `operand` is an operand that is not used by the dataflow library.
* Ignored operands are not recognized as uses by SSA, and they don't have a
* corresponding `(Indirect)OperandNode`.
*/
predicate ignoreOperand(Operand operand) {
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
operand instanceof MemoryOperand
}
/**
* Holds if `instr` is an instruction that is not used by the dataflow library.
* Ignored instructions are not recognized as reads/writes by SSA, and they
* don't have a corresponding `(Indirect)InstructionNode`.
*/
predicate ignoreInstruction(Instruction instr) {
DataFlowImplCommon::forceCachingInSameStage() and
(
instr instanceof CallSideEffectInstruction or
instr instanceof CallReadSideEffectInstruction or
instr instanceof ExitFunctionInstruction or
instr instanceof EnterFunctionInstruction or
instr instanceof WriteSideEffectInstruction or
instr instanceof PhiInstruction or
instr instanceof ReadSideEffectInstruction or
instr instanceof ChiInstruction or
instr instanceof InitializeIndirectionInstruction or
instr instanceof AliasedDefinitionInstruction or
instr instanceof AliasedUseInstruction or
instr instanceof InitializeNonLocalInstruction or
instr instanceof ReturnIndirectionInstruction or
instr instanceof UninitializedGroupInstruction
)
}
/**
* Gets the C++ type of `this` in the member function `f`.
* The result is a glvalue if `isGLValue` is true, and
@@ -55,26 +20,6 @@ private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) {
result.hasType(f.getTypeOfThis(), isGLValue)
}
/**
* Gets the C++ type of the instruction `i`.
*
* This is equivalent to `i.getResultLanguageType()` with the exception
* of instructions that directly references a `this` IRVariable. In this
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
* predicate gives the expected type (i.e., a potentially cv-qualified
* type `A*` where `A` is the declaring type of the member function that
* contains `i`).
*/
cached
CppType getResultLanguageType(Instruction i) {
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
then
if i.isGLValue()
then result = getThisType(i.getEnclosingFunction(), true)
else result = getThisType(i.getEnclosingFunction(), false)
else result = i.getResultLanguageType()
}
/**
* Gets the C++ type of the operand `operand`.
* This is equivalent to the type of the operand's defining instruction.
@@ -347,10 +292,6 @@ predicate isWrite(Node0Impl value, Operand address, boolean certain) {
)
}
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
}
newtype TBaseSourceVariable =
// Each IR variable gets its own source variable
TBaseIRVariable(IRVariable var) or
@@ -572,6 +513,69 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns
cached
private module Cached {
/**
* Holds if `operand` is an operand that is not used by the dataflow library.
* Ignored operands are not recognized as uses by SSA, and they don't have a
* corresponding `(Indirect)OperandNode`.
*/
cached
predicate ignoreOperand(Operand operand) {
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
operand instanceof MemoryOperand
}
/**
* Holds if `instr` is an instruction that is not used by the dataflow library.
* Ignored instructions are not recognized as reads/writes by SSA, and they
* don't have a corresponding `(Indirect)InstructionNode`.
*/
cached
predicate ignoreInstruction(Instruction instr) {
DataFlowImplCommon::forceCachingInSameStage() and
(
instr instanceof CallSideEffectInstruction or
instr instanceof CallReadSideEffectInstruction or
instr instanceof ExitFunctionInstruction or
instr instanceof EnterFunctionInstruction or
instr instanceof WriteSideEffectInstruction or
instr instanceof PhiInstruction or
instr instanceof ReadSideEffectInstruction or
instr instanceof ChiInstruction or
instr instanceof InitializeIndirectionInstruction or
instr instanceof AliasedDefinitionInstruction or
instr instanceof AliasedUseInstruction or
instr instanceof InitializeNonLocalInstruction or
instr instanceof ReturnIndirectionInstruction or
instr instanceof UninitializedGroupInstruction
)
}
cached
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
}
/**
* Gets the C++ type of the instruction `i`.
*
* This is equivalent to `i.getResultLanguageType()` with the exception
* of instructions that directly references a `this` IRVariable. In this
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
* predicate gives the expected type (i.e., a potentially cv-qualified
* type `A*` where `A` is the declaring type of the member function that
* contains `i`).
*/
cached
CppType getResultLanguageType(Instruction i) {
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
then
if i.isGLValue()
then result = getThisType(i.getEnclosingFunction(), true)
else result = getThisType(i.getEnclosingFunction(), false)
else result = i.getResultLanguageType()
}
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
private predicate isConversion(Operand op) {
exists(Instruction def, Operand use |

View File

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

View File

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

View File

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

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 typeBounds(ArithmeticType t, float lb, float ub) {
private predicate typeBounds0(ArithmeticType t, float lb, float ub) {
exists(IntegralType integralType, float limit |
integralType = t and limit = 2.pow(8 * integralType.getSize())
|
@@ -423,6 +423,42 @@ private predicate typeBounds(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 the in the tests).
result = 2.0.pow(40)
// specific observed customer databases (and in the tests).
result = 2.0.pow(29)
}
/** Gets the maximum number of bounds possible for `t` when widening is used. */
@@ -552,34 +552,47 @@ 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 }
// e2
// e3
//
// 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))
// 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
)
or
def.isPhiNode(v) and
not isGuardPhiWithBound(def, v, _) and
@@ -2180,6 +2193,16 @@ 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. */
@@ -2208,7 +2231,7 @@ private module Debug {
*/
predicate countGetLowerBoundsImpl(Expr e, int n) {
e = getRelevantLocatable() and
n = strictcount(float lb | lb = getLowerBoundsImpl(e) | lb)
n = SimpleRangeAnalysisInternal::countNrOfLowerBounds(e)
}
float debugNrOfBounds(Expr e) {

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
class Element extends @element {
string toString() { none() }
}
class Trap extends @trap {
string toString() { none() }
}
from Element e, Trap trap
where in_trap(e, trap)
select e, trap

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
newtype TSourceFile = MkSourceFile(string name) { source_file_uses_trap(name, _) }
module FreshSourceFile = QlBuiltins::NewEntity<TSourceFile>;
class SourceFile extends FreshSourceFile::EntityId {
string toString() { none() }
}
class Trap extends @trap {
string toString() { none() }
}
query predicate mk_source_file_name(SourceFile source_file, string name) {
source_file = FreshSourceFile::map(MkSourceFile(name))
}
query predicate mk_source_file_uses_trap(SourceFile source_file, Trap trap) {
exists(string name |
source_file_uses_trap(name, trap) and
mk_source_file_name(source_file, name)
)
}

View File

@@ -0,0 +1,6 @@
description: Add source_file_name
compatibility: backwards
source_file_uses_trap.rel: run source_files.ql mk_source_file_uses_trap
source_file_name.rel: run source_files.ql mk_source_file_name
in_trap.rel: delete
in_trap_or_tag.rel: run in_trap_or_tag.ql

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -308,3 +308,37 @@ 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 problem
* @kind path-problem
* @problem.severity warning
* @id cpp/leap-year/unchecked-after-arithmetic-year-modification
* @precision medium
@@ -11,49 +11,844 @@
import cpp
import LeapYear
import semmle.code.cpp.controlflow.IRGuards
from Variable var, LeapYearFieldAccess yfa
where
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*()
)
/**
* 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
// 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
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
)
select yfa,
"Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found.",
yfa.getTarget(), yfa.getTarget().toString(), var, var.toString()
}
/**
* 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
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())
)
select sink, src, sink,
"Year field has been modified, but no appropriate check for LeapYear was found."

View File

@@ -44,23 +44,9 @@ 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

@@ -15,6 +15,7 @@
import cpp
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
/** Gets a loop that contains `e`. */
Loop getAnEnclosingLoopOfExpr(Expr e) { result = getAnEnclosingLoopOfStmt(e.getEnclosingStmt()) }
@@ -45,9 +46,9 @@ private Expr getExpr(DataFlow::Node node) {
or
result = node.asOperand().getUse().getAst()
or
result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst()
result = node.(RawIndirectInstruction).getInstruction().getAst()
or
result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst()
result = node.(RawIndirectOperand).getOperand().getUse().getAst()
}
/**
@@ -208,7 +209,7 @@ class LoopWithAlloca extends Stmt {
this.conditionRequiresInequality(va, _, _) and
DataFlow::localFlow(result, DataFlow::exprNode(va)) and
// Phi nodes will be preceded by nodes that represent actual definitions
not result instanceof DataFlow::SsaSynthNode and
not result instanceof SsaSynthNode and
// A source is outside the loop if it's not inside the loop
not exists(Expr e | e = getExpr(result) | this = getAnEnclosingLoopOfExpr(e))
)

View File

@@ -16,17 +16,15 @@
import cpp
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow
import PathGraph
import ReturnStackAllocatedMemory::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%"])
}
class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
ReturnStackAllocatedMemoryConfig() { this = "ReturnStackAllocatedMemoryConfig" }
override predicate isSource(Instruction source) {
module ReturnStackAllocatedMemoryConfig implements MustFlow::ConfigSig {
predicate isSource(Instruction source) {
exists(Function func |
// Rule out FPs caused by extraction errors.
not func.hasErrors() and
@@ -50,7 +48,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
)
}
override predicate isSink(Operand sink) {
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
@@ -72,7 +70,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
// int* px = id(&x);
// }
// ```
override predicate allowInterproceduralFlow() { none() }
predicate allowInterproceduralFlow() { none() }
/**
* This configuration intentionally conflates addresses of fields and their object, and pointer offsets
@@ -87,20 +85,22 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
* }
* ```
*/
override predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
node2.(FieldAddressInstruction).getObjectAddressOperand() = node1
or
node2.(PointerOffsetInstruction).getLeftOperand() = node1
}
override predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType }
predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType }
}
module ReturnStackAllocatedMemory = MustFlow::Global<ReturnStackAllocatedMemoryConfig>;
from
MustFlowPathNode source, MustFlowPathNode sink, Instruction instr,
ReturnStackAllocatedMemoryConfig conf
ReturnStackAllocatedMemory::PathNode source, ReturnStackAllocatedMemory::PathNode sink,
Instruction instr
where
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
ReturnStackAllocatedMemory::flowPath(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 PathGraph
import UninitializedLocal::PathGraph
/**
* Auxiliary predicate: Types that don't require initialization
@@ -70,25 +70,26 @@ predicate isSinkImpl(Instruction sink, VariableAccess va) {
)
}
class MustFlow extends MustFlowConfiguration {
MustFlow() { this = "MustFlow" }
override predicate isSource(Instruction source) {
module UninitializedLocalConfig implements MustFlow::ConfigSig {
predicate isSource(Instruction source) {
source instanceof UninitializedInstruction and
exists(Type t | t = source.getResultType() | not allocatedType(t))
}
override predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
override predicate allowInterproceduralFlow() { none() }
predicate allowInterproceduralFlow() { none() }
override predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
}
module UninitializedLocal = MustFlow::Global<UninitializedLocalConfig>;
from
VariableAccess va, LocalVariable v, MustFlow conf, MustFlowPathNode source, MustFlowPathNode sink
VariableAccess va, LocalVariable v, UninitializedLocal::PathNode source,
UninitializedLocal::PathNode sink
where
conf.hasFlowPath(source, sink) and
UninitializedLocal::flowPath(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 PathGraph
import UnsafeUseOfThis::PathGraph
class UnsafeUseOfThisConfig extends MustFlowConfiguration {
UnsafeUseOfThisConfig() { this = "UnsafeUseOfThisConfig" }
module UnsafeUseOfThisConfig implements MustFlow::ConfigSig {
predicate isSource(Instruction source) { isSource(source, _, _) }
override predicate isSource(Instruction source) { isSource(source, _, _) }
override predicate isSink(Operand sink) { isSink(sink, _) }
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,19 +66,17 @@ predicate isSource(InitializeParameterInstruction source, string msg, Class c) {
* - `msg` is a string describing whether `source` is from a constructor or destructor.
*/
predicate flows(
MustFlowPathNode source, string msg, Class sourceClass, MustFlowPathNode sink,
UnsafeUseOfThis::PathNode source, string msg, Class sourceClass, UnsafeUseOfThis::PathNode sink,
CallInstruction call
) {
exists(UnsafeUseOfThisConfig conf |
conf.hasFlowPath(source, sink) and
isSource(source.getInstruction(), msg, sourceClass) and
isSink(sink.getInstruction().getAUse(), call)
)
UnsafeUseOfThis::flowPath(source, sink) and
isSource(source.getInstruction(), msg, sourceClass) and
isSink(sink.getInstruction().getAUse(), call)
}
from
MustFlowPathNode source, MustFlowPathNode sink, CallInstruction call, string msg,
Class sourceClass
UnsafeUseOfThis::PathNode source, UnsafeUseOfThis::PathNode 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

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.10
lastReleaseVersion: 1.5.11

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 1.5.11-dev
version: 1.5.12-dev
groups:
- cpp
- queries

View File

@@ -8,6 +8,7 @@ private import semmle.code.cpp.dataflow.ExternalFlow as ExternalFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes as DataFlowNodes
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
private import semmle.code.cpp.dataflow.new.TaintTracking as Tt
@@ -403,7 +404,7 @@ private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
}
predicate apiSource(DataFlow::Node source) {
DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1)
DataFlowPrivate::nodeHasOperand(source, any(DataFlowNodes::FieldAddress fa), 1)
or
source instanceof DataFlow::ParameterNode
}
@@ -416,7 +417,7 @@ private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
result = "Argument[" + DataFlow::repeatStars(indirectionIndex) + argumentIndex + "]"
)
or
DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1) and
DataFlowPrivate::nodeHasOperand(source, any(DataFlowNodes::FieldAddress fa), 1) and
result = qualifierString()
}

View File

@@ -15,7 +15,10 @@ predicate instructionGuardChecks(IRGuardCondition gc, Instruction checked, boole
module BarrierGuard = DataFlow::InstructionBarrierGuard<instructionGuardChecks/3>;
predicate indirectBarrierGuard(DataFlow::Node node, string s) {
node = BarrierGuard::getAnIndirectBarrierNode(_) and
// This any(...) could technically be removed, but it helps us verify that we don't
// accidentially change the API of this predicate (for instance, by having
// the column be a unit parameter).
node = BarrierGuard::getAnIndirectBarrierNode(any(int indirectionIndex)) and
if node.isGLValue()
then s = "glval<" + node.getType().toString().replaceAll(" ", "") + ">"
else s = node.getType().toString().replaceAll(" ", "")

View File

@@ -1,6 +1,7 @@
import testModels
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
string describe(DataFlow::Node n) {
n instanceof ParameterNode and result = "ParameterNode"

View File

@@ -2,8 +2,20 @@ import cpp
import utils.test.InlineExpectationsTest
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
query predicate estimateNrOfBounds(Expr e, float nrOfBounds) {
nrOfBounds = SimpleRangeAnalysisInternal::estimateNrOfBounds(e)
query predicate estimateNrOfBounds(
Expr e, float nrOfBounds, float actualNrOfLowerBounds, float actualNrOfUpperBounds
) {
nrOfBounds = SimpleRangeAnalysisInternal::estimateNrOfBounds(e) and
(
actualNrOfLowerBounds = SimpleRangeAnalysisInternal::countNrOfLowerBounds(e)
or
not exists(SimpleRangeAnalysisInternal::countNrOfLowerBounds(e)) and actualNrOfLowerBounds = -1
) and
(
actualNrOfUpperBounds = SimpleRangeAnalysisInternal::countNrOfUpperBounds(e)
or
not exists(SimpleRangeAnalysisInternal::countNrOfUpperBounds(e)) and actualNrOfUpperBounds = -1
)
}
/**
@@ -14,8 +26,14 @@ private predicate nonFunctionalNrOfBounds(Expr e) {
strictcount(SimpleRangeAnalysisInternal::estimateNrOfBounds(e)) > 1
}
private predicate nrOfBoundsNotEq1(Expr e, int n) {
e.getFile().getBaseName() = "test_nr_of_bounds.cpp" and
n = count(SimpleRangeAnalysisInternal::estimateNrOfBounds(e)) and
n != 1
}
module FunctionalityTest implements TestSig {
string getARelevantTag() { result = "nonFunctionalNrOfBounds" }
string getARelevantTag() { result = ["nonFunctionalNrOfBounds", "bounds"] }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(Expr e |
@@ -25,6 +43,14 @@ module FunctionalityTest implements TestSig {
tag = "nonFunctionalNrOfBounds" and
value = ""
)
or
exists(Expr e, int n |
nrOfBoundsNotEq1(e, n) and
location = e.getLocation() and
element = e.toString() and
tag = "bounds" and
value = n.toString()
)
}
}

View File

@@ -77,77 +77,77 @@
| test.c:426:22:426:82 | ... ? ... : ... | 0.13204114 | 0.42186276 | 0.13204114 |
| test.c:426:26:426:69 | ... ? ... : ... | 0.42186276 | 0.42186276 | 0.44996679 |
| test.c:426:30:426:56 | ... ? ... : ... | 0.42186276 | 0.42186276 | 0.53843358 |
| test.c:468:4:642:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:468:5:470:49 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:471:6:553:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:472:8:490:41 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:475:10:479:21 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:475:31:475:79 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:477:13:479:21 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:484:12:489:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:485:12:485:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:487:15:489:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:491:6:510:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:494:8:498:19 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:494:29:494:77 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:496:11:498:19 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:499:6:499:54 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:503:10:507:21 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:503:31:503:79 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:505:13:507:21 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:508:9:510:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:512:10:531:43 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:515:12:520:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:516:12:516:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:518:15:520:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:525:14:530:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:526:14:526:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:528:17:530:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:532:9:553:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:535:14:540:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:536:14:536:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:538:17:540:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:541:12:541:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:545:12:550:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:546:12:546:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:548:15:550:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:551:11:553:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:554:9:556:51 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:557:9:642:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:558:14:577:47 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:561:16:566:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:562:16:562:64 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:564:19:566:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:571:18:576:29 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:572:18:572:66 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:574:21:576:29 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:578:12:599:29 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:581:14:586:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:582:14:582:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:584:17:586:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:587:12:587:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:591:16:596:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:592:16:592:64 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:594:19:596:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:597:15:599:29 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:601:12:620:45 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:604:14:609:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:605:14:605:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:607:17:609:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:614:16:619:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:615:16:615:64 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:617:19:619:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:621:11:642:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:624:16:629:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:625:16:625:64 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:627:19:629:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:630:14:630:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:634:14:639:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:635:14:635:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:637:17:639:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:640:13:642:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:668:20:668:36 | ... ? ... : ... | 0.0 | 0.0 | 100.0 |
| test.c:880:5:880:14 | ... ? ... : ... | 0.0 | 1.0 | 0.0 |
| test.c:881:5:881:14 | ... ? ... : ... | 0.0 | 0.0 | 1.0 |
| test.c:485:4:659:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:485:5:487:49 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:488:6:570:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:489:8:507:41 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:492:10:496:21 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:492:31:492:79 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:494:13:496:21 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:501:12:506:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:502:12:502:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:504:15:506:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:508:6:527:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:511:8:515:19 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:511:29:511:77 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:513:11:515:19 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:516:6:516:54 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:520:10:524:21 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:520:31:520:79 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:522:13:524:21 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:525:9:527:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:529:10:548:43 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:532:12:537:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:533:12:533:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:535:15:537:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:542:14:547:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:543:14:543:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:545:17:547:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:549:9:570:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:552:14:557:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:553:14:553:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:555:17:557:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:558:12:558:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:562:12:567:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:563:12:563:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:565:15:567:23 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:568:11:570:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:571:9:573:51 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:574:9:659:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:575:14:594:47 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:578:16:583:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:579:16:579:64 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:581:19:583:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:588:18:593:29 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:589:18:589:66 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:591:21:593:29 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:595:12:616:29 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:598:14:603:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:599:14:599:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:601:17:603:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:604:12:604:60 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:608:16:613:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:609:16:609:64 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:611:19:613:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:614:15:616:29 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:618:12:637:45 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:621:14:626:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:622:14:622:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:624:17:626:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:631:16:636:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:632:16:632:64 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:634:19:636:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:638:11:659:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:641:16:646:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:642:16:642:64 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:644:19:646:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:647:14:647:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:651:14:656:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:652:14:652:62 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:654:17:656:25 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:657:13:659:27 | ... ? ... : ... | 0.0 | 0.0 | 0.0 |
| test.c:685:20:685:36 | ... ? ... : ... | 0.0 | 0.0 | 100.0 |
| test.c:897:5:897:14 | ... ? ... : ... | 0.0 | 1.0 | 0.0 |
| test.c:898:5:898:14 | ... ? ... : ... | 0.0 | 0.0 | 1.0 |
| test.cpp:121:3:121:12 | ... ? ... : ... | 0.0 | 1.0 | 0.0 |
| test.cpp:122:3:122:12 | ... ? ... : ... | 0.0 | 0.0 | 1.0 |

View File

@@ -77,77 +77,77 @@
| test.c:426:22:426:82 | ... ? ... : ... | 0.53843358 | 0.53843358 | 0.13204114 |
| test.c:426:26:426:69 | ... ? ... : ... | 0.53843358 | 0.53843358 | 0.44996679 |
| test.c:426:30:426:56 | ... ? ... : ... | 0.53843358 | 0.42186276 | 0.53843358 |
| test.c:468:4:642:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:468:5:470:49 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:471:6:553:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:472:8:490:41 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:475:10:479:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:475:31:475:79 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:477:13:479:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:484:12:489:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:485:12:485:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:487:15:489:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:491:6:510:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:494:8:498:19 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:494:29:494:77 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:496:11:498:19 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:499:6:499:54 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:503:10:507:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:503:31:503:79 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:505:13:507:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:508:9:510:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:512:10:531:43 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:515:12:520:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:516:12:516:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:518:15:520:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:525:14:530:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:526:14:526:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:528:17:530:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:532:9:553:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:535:14:540:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:536:14:536:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:538:17:540:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:541:12:541:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:545:12:550:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:546:12:546:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:548:15:550:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:551:11:553:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:554:9:556:51 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:557:9:642:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:558:14:577:47 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:561:16:566:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:562:16:562:64 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:564:19:566:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:571:18:576:29 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:572:18:572:66 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:574:21:576:29 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:578:12:599:29 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:581:14:586:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:582:14:582:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:584:17:586:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:587:12:587:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:591:16:596:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:592:16:592:64 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:594:19:596:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:597:15:599:29 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:601:12:620:45 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:604:14:609:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:605:14:605:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:607:17:609:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:614:16:619:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:615:16:615:64 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:617:19:619:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:621:11:642:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:624:16:629:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:625:16:625:64 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:627:19:629:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:630:14:630:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:634:14:639:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:635:14:635:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:637:17:639:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:640:13:642:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:668:20:668:36 | ... ? ... : ... | 100.0 | 99.0 | 100.0 |
| test.c:880:5:880:14 | ... ? ... : ... | 32767.0 | 32767.0 | 0.0 |
| test.c:881:5:881:14 | ... ? ... : ... | 32767.0 | 0.0 | 32767.0 |
| test.c:485:4:659:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:485:5:487:49 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:488:6:570:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:489:8:507:41 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:492:10:496:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:492:31:492:79 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:494:13:496:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:501:12:506:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:502:12:502:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:504:15:506:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:508:6:527:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:511:8:515:19 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:511:29:511:77 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:513:11:515:19 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:516:6:516:54 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:520:10:524:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:520:31:520:79 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:522:13:524:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:525:9:527:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:529:10:548:43 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:532:12:537:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:533:12:533:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:535:15:537:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:542:14:547:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:543:14:543:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:545:17:547:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:549:9:570:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:552:14:557:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:553:14:553:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:555:17:557:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:558:12:558:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:562:12:567:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:563:12:563:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:565:15:567:23 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:568:11:570:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:571:9:573:51 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:574:9:659:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:575:14:594:47 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:578:16:583:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:579:16:579:64 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:581:19:583:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:588:18:593:29 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:589:18:589:66 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:591:21:593:29 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:595:12:616:29 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:598:14:603:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:599:14:599:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:601:17:603:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:604:12:604:60 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:608:16:613:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:609:16:609:64 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:611:19:613:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:614:15:616:29 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:618:12:637:45 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:621:14:626:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:622:14:622:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:624:17:626:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:631:16:636:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:632:16:632:64 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:634:19:636:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:638:11:659:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:641:16:646:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:642:16:642:64 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:644:19:646:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:647:14:647:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:651:14:656:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:652:14:652:62 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:654:17:656:25 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:657:13:659:27 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 4.294967295E9 |
| test.c:685:20:685:36 | ... ? ... : ... | 100.0 | 99.0 | 100.0 |
| test.c:897:5:897:14 | ... ? ... : ... | 32767.0 | 32767.0 | 0.0 |
| test.c:898:5:898:14 | ... ? ... : ... | 32767.0 | 0.0 | 32767.0 |
| test.cpp:121:3:121:12 | ... ? ... : ... | 32767.0 | 32767.0 | 0.0 |
| test.cpp:122:3:122:12 | ... ? ... : ... | 32767.0 | 0.0 | 32767.0 |

View File

@@ -446,6 +446,23 @@ int repeated_if_statements(unsigned int rhs) {
return rhs; // rhs has 6 bounds
}
int repeated_if_else_statements(unsigned int rhs) {
// Test how many bounds we estimate for repeated `if`-`else` statements that
// guard the same variable.
if (rhs < 10) { rhs << 1; } else { rhs << 2; }
if (rhs < 11) { rhs << 1; } else { rhs << 2; }
if (rhs < 12) { rhs << 1; } else { rhs << 2; }
if (rhs < 13) { rhs << 1; } else { rhs << 2; }
if (rhs < 14) { rhs << 1; } else { rhs << 2; }
if (rhs < 15) { rhs << 1; } else { rhs << 2; }
if (rhs < 16) { rhs << 1; } else { rhs << 2; }
if (rhs < 17) { rhs << 1; } else { rhs << 2; }
if (rhs < 18) { rhs << 1; } else { rhs << 2; }
if (rhs < 19) { rhs << 1; } else { rhs << 2; }
if (rhs < 20) { rhs << 1; } else { rhs << 2; }
return rhs; // rhs has 12 bounds
}
int ne_phi_nodes(int a, int b) {
if (a == 17) {
if (b == 23) {
@@ -972,3 +989,15 @@ void test_overflow() {
out(y);
}
}
enum MY_ENUM_2 {
A = 0x1,
B = 0x2,
C = 0x4,
D = 0x8,
E = 0x10
};
void test_enum(enum MY_ENUM_2 e) {
out(e);
}

View File

@@ -0,0 +1,73 @@
enum MY_ENUM {
A = 0x1,
B = 0x2,
C = 0x4,
D = 0x8,
E = 0x10,
F = 0x20,
G = 0x40,
H = 0x80,
I = 0x100,
J = 0x200,
L = 0x400,
M = 0x800,
N = 0x1000,
O = 0x2000,
P = 0x4000,
Q = 0x8000,
R = 0x10000,
S = 0x20000,
T = 0x40000,
U = 0x80000,
V = 0x100000,
W = 0x200000,
X = 0x400000,
Y = 0x800000,
Z = 0x1000000,
AA = 0x2000000,
AB = 0x4000000,
AC = 0x8000000,
AD = 0x10000000,
AE = 0x20000000
};
typedef unsigned int MY_ENUM_FLAGS;
MY_ENUM_FLAGS check_and_subs(MY_ENUM_FLAGS x)
{
#define CHECK_AND_SUB(flag) if ((x & flag) == flag) { x -= flag; }
CHECK_AND_SUB(A);
CHECK_AND_SUB(B);
CHECK_AND_SUB(C);
CHECK_AND_SUB(D);
CHECK_AND_SUB(E);
CHECK_AND_SUB(F);
CHECK_AND_SUB(G);
CHECK_AND_SUB(H);
CHECK_AND_SUB(I);
CHECK_AND_SUB(J);
CHECK_AND_SUB(L);
CHECK_AND_SUB(M);
CHECK_AND_SUB(N);
CHECK_AND_SUB(O);
CHECK_AND_SUB(P);
CHECK_AND_SUB(Q);
CHECK_AND_SUB(R);
CHECK_AND_SUB(S);
CHECK_AND_SUB(T);
CHECK_AND_SUB(U);
CHECK_AND_SUB(V);
CHECK_AND_SUB(W);
CHECK_AND_SUB(X);
CHECK_AND_SUB(Y);
CHECK_AND_SUB(Z);
CHECK_AND_SUB(AA);
CHECK_AND_SUB(AB);
CHECK_AND_SUB(AC);
CHECK_AND_SUB(AD);
CHECK_AND_SUB(AE);
#undef CHECK_AND_SUB
return x;
}

View File

@@ -1,15 +1,143 @@
| test.cpp:314:5:314:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:309:13:309:14 | st | st |
| test.cpp:327:5:327:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:322:13:322:14 | st | st |
| test.cpp:338:6:338:10 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:333:62:333:63 | st | st |
| test.cpp:484:5:484:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:480:13:480:14 | st | st |
| test.cpp:497:5:497:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:492:13:492:14 | st | st |
| test.cpp:509:5:509:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:505:13:505:14 | st | st |
| test.cpp:606:11:606:17 | tm_year | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:56:6:56:12 | tm_year | tm_year | test.cpp:602:12:602:19 | timeinfo | timeinfo |
| test.cpp:634:11:634:17 | tm_year | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:56:6:56:12 | tm_year | tm_year | test.cpp:628:12:628:19 | timeinfo | timeinfo |
| test.cpp:636:11:636:17 | tm_year | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:56:6:56:12 | tm_year | tm_year | test.cpp:628:12:628:19 | timeinfo | timeinfo |
| test.cpp:640:5:640:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:629:13:629:14 | st | st |
| test.cpp:642:5:642:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:629:13:629:14 | st | st |
| test.cpp:718:5:718:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:712:13:712:14 | st | st |
| test.cpp:731:5:731:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:725:13:725:14 | st | st |
| test.cpp:732:5:732:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:725:13:725:14 | st | st |
| test.cpp:733:5:733:9 | wYear | Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found. | test.cpp:12:7:12:11 | wYear | wYear | test.cpp:725:13:725:14 | st | st |
#select
| test.cpp:422:2:422:14 | ... += ... | test.cpp:422:2:422:14 | ... += ... | test.cpp:422:2:422:14 | ... += ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:440:2:440:11 | ... ++ | test.cpp:440:2:440:11 | ... ++ | test.cpp:440:2:440:11 | ... ++ | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:456:2:456:12 | ... ++ | test.cpp:456:2:456:12 | ... ++ | test.cpp:456:2:456:12 | ... ++ | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:681:2:681:23 | ... += ... | test.cpp:681:2:681:23 | ... += ... | test.cpp:681:2:681:23 | ... += ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:813:2:813:40 | ... = ... | test.cpp:813:21:813:40 | ... + ... | test.cpp:813:2:813:40 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:818:2:818:24 | ... = ... | test.cpp:818:13:818:24 | ... + ... | test.cpp:818:2:818:24 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:951:3:951:25 | ... = ... | test.cpp:951:14:951:25 | ... + ... | test.cpp:951:3:951:25 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:969:3:969:12 | ... ++ | test.cpp:969:3:969:12 | ... ++ | test.cpp:969:3:969:12 | ... ++ | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1031:2:1031:11 | ... ++ | test.cpp:1031:2:1031:11 | ... ++ | test.cpp:1031:2:1031:11 | ... ++ | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1051:16:1051:23 | increment_arg output argument | test.cpp:1039:2:1039:4 | ... ++ | test.cpp:1051:16:1051:23 | increment_arg output argument | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1055:27:1055:35 | increment_arg_by_pointer output argument | test.cpp:1043:2:1043:7 | ... ++ | test.cpp:1055:27:1055:35 | increment_arg_by_pointer output argument | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1109:2:1109:26 | ... = ... | test.cpp:1109:14:1109:26 | ... - ... | test.cpp:1109:2:1109:26 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1160:2:1160:19 | ... = ... | test.cpp:1158:2:1158:15 | ... += ... | test.cpp:1160:2:1160:19 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1199:2:1199:28 | ... = ... | test.cpp:1199:16:1199:28 | ... + ... | test.cpp:1199:2:1199:28 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1214:2:1214:28 | ... = ... | test.cpp:1214:16:1214:28 | ... + ... | test.cpp:1214:2:1214:28 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1228:2:1228:28 | ... = ... | test.cpp:1228:16:1228:28 | ... + ... | test.cpp:1228:2:1228:28 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1242:2:1242:26 | ... = ... | test.cpp:1242:14:1242:26 | ... + ... | test.cpp:1242:2:1242:26 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1256:2:1256:26 | ... = ... | test.cpp:1256:14:1256:26 | ... + ... | test.cpp:1256:2:1256:26 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1262:2:1262:28 | ... = ... | test.cpp:1262:16:1262:28 | ... + ... | test.cpp:1262:2:1262:28 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1274:2:1274:28 | ... = ... | test.cpp:1274:16:1274:28 | ... + ... | test.cpp:1274:2:1274:28 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1287:2:1287:26 | ... = ... | test.cpp:1287:14:1287:26 | ... + ... | test.cpp:1287:2:1287:26 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1299:2:1299:26 | ... = ... | test.cpp:1299:14:1299:26 | ... + ... | test.cpp:1299:2:1299:26 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1341:2:1341:17 | ... = ... | test.cpp:1432:12:1432:17 | ... + ... | test.cpp:1341:2:1341:17 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1341:2:1341:17 | ... = ... | test.cpp:1446:9:1446:16 | ... + ... | test.cpp:1341:2:1341:17 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1341:2:1341:17 | ... = ... | test.cpp:1458:9:1458:16 | ... + ... | test.cpp:1341:2:1341:17 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1515:2:1515:15 | ... = ... | test.cpp:1512:2:1512:10 | ... += ... | test.cpp:1515:2:1515:15 | ... = ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1545:2:1545:22 | ... += ... | test.cpp:1545:2:1545:22 | ... += ... | test.cpp:1545:2:1545:22 | ... += ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1553:2:1553:22 | ... += ... | test.cpp:1553:2:1553:22 | ... += ... | test.cpp:1553:2:1553:22 | ... += ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1632:2:1632:22 | ... += ... | test.cpp:1632:2:1632:22 | ... += ... | test.cpp:1632:2:1632:22 | ... += ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1644:2:1644:22 | ... += ... | test.cpp:1644:2:1644:22 | ... += ... | test.cpp:1644:2:1644:22 | ... += ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1677:2:1677:22 | ... += ... | test.cpp:1677:2:1677:22 | ... += ... | test.cpp:1677:2:1677:22 | ... += ... | Year field has been modified, but no appropriate check for LeapYear was found. |
| test.cpp:1753:2:1753:22 | ... += ... | test.cpp:1753:2:1753:22 | ... += ... | test.cpp:1753:2:1753:22 | ... += ... | Year field has been modified, but no appropriate check for LeapYear was found. |
edges
| test.cpp:813:21:813:40 | ... + ... | test.cpp:813:2:813:40 | ... = ... | provenance | |
| test.cpp:818:13:818:24 | ... + ... | test.cpp:818:2:818:24 | ... = ... | provenance | |
| test.cpp:951:14:951:25 | ... + ... | test.cpp:951:3:951:25 | ... = ... | provenance | |
| test.cpp:1038:26:1038:26 | *x | test.cpp:1051:16:1051:23 | increment_arg output argument | provenance | |
| test.cpp:1039:2:1039:4 | ... ++ | test.cpp:1038:26:1038:26 | *x | provenance | |
| test.cpp:1042:37:1042:37 | *x | test.cpp:1055:27:1055:35 | increment_arg_by_pointer output argument | provenance | |
| test.cpp:1043:2:1043:7 | ... ++ | test.cpp:1042:37:1042:37 | *x | provenance | |
| test.cpp:1109:14:1109:26 | ... - ... | test.cpp:1109:2:1109:26 | ... = ... | provenance | |
| test.cpp:1158:2:1158:15 | ... += ... | test.cpp:1160:2:1160:19 | ... = ... | provenance | |
| test.cpp:1199:16:1199:28 | ... + ... | test.cpp:1199:2:1199:28 | ... = ... | provenance | |
| test.cpp:1214:16:1214:28 | ... + ... | test.cpp:1214:2:1214:28 | ... = ... | provenance | |
| test.cpp:1228:16:1228:28 | ... + ... | test.cpp:1228:2:1228:28 | ... = ... | provenance | |
| test.cpp:1242:14:1242:26 | ... + ... | test.cpp:1242:2:1242:26 | ... = ... | provenance | |
| test.cpp:1256:14:1256:26 | ... + ... | test.cpp:1256:2:1256:26 | ... = ... | provenance | |
| test.cpp:1262:16:1262:28 | ... + ... | test.cpp:1262:2:1262:28 | ... = ... | provenance | |
| test.cpp:1274:16:1274:28 | ... + ... | test.cpp:1274:2:1274:28 | ... = ... | provenance | |
| test.cpp:1287:14:1287:26 | ... + ... | test.cpp:1287:2:1287:26 | ... = ... | provenance | |
| test.cpp:1299:14:1299:26 | ... + ... | test.cpp:1299:2:1299:26 | ... = ... | provenance | |
| test.cpp:1338:20:1338:23 | year | test.cpp:1341:2:1341:17 | ... = ... | provenance | |
| test.cpp:1351:15:1351:22 | ... + ... | test.cpp:1351:3:1351:22 | ... = ... | provenance | |
| test.cpp:1356:12:1356:17 | ... + ... | test.cpp:1338:20:1338:23 | year | provenance | |
| test.cpp:1365:15:1365:22 | ... + ... | test.cpp:1365:3:1365:22 | ... = ... | provenance | |
| test.cpp:1375:3:1375:20 | ... = ... | test.cpp:1377:12:1377:18 | yeartmp | provenance | |
| test.cpp:1375:13:1375:20 | ... + ... | test.cpp:1375:3:1375:20 | ... = ... | provenance | |
| test.cpp:1377:12:1377:18 | yeartmp | test.cpp:1338:20:1338:23 | year | provenance | |
| test.cpp:1420:15:1420:22 | ... + ... | test.cpp:1420:3:1420:22 | ... = ... | provenance | |
| test.cpp:1425:12:1425:17 | ... + ... | test.cpp:1338:20:1338:23 | year | provenance | |
| test.cpp:1432:12:1432:17 | ... + ... | test.cpp:1338:20:1338:23 | year | provenance | |
| test.cpp:1446:2:1446:16 | ... = ... | test.cpp:1450:3:1450:18 | ... = ... | provenance | |
| test.cpp:1446:2:1446:16 | ... = ... | test.cpp:1455:12:1455:15 | year | provenance | |
| test.cpp:1446:9:1446:16 | ... + ... | test.cpp:1446:2:1446:16 | ... = ... | provenance | |
| test.cpp:1455:12:1455:15 | year | test.cpp:1338:20:1338:23 | year | provenance | |
| test.cpp:1458:2:1458:16 | ... = ... | test.cpp:1464:12:1464:15 | year | provenance | |
| test.cpp:1458:9:1458:16 | ... + ... | test.cpp:1458:2:1458:16 | ... = ... | provenance | |
| test.cpp:1464:12:1464:15 | year | test.cpp:1338:20:1338:23 | year | provenance | |
| test.cpp:1512:2:1512:10 | ... += ... | test.cpp:1515:2:1515:15 | ... = ... | provenance | |
nodes
| test.cpp:422:2:422:14 | ... += ... | semmle.label | ... += ... |
| test.cpp:440:2:440:11 | ... ++ | semmle.label | ... ++ |
| test.cpp:456:2:456:12 | ... ++ | semmle.label | ... ++ |
| test.cpp:482:3:482:12 | ... ++ | semmle.label | ... ++ |
| test.cpp:681:2:681:23 | ... += ... | semmle.label | ... += ... |
| test.cpp:813:2:813:40 | ... = ... | semmle.label | ... = ... |
| test.cpp:813:21:813:40 | ... + ... | semmle.label | ... + ... |
| test.cpp:818:2:818:24 | ... = ... | semmle.label | ... = ... |
| test.cpp:818:13:818:24 | ... + ... | semmle.label | ... + ... |
| test.cpp:872:4:872:15 | ... ++ | semmle.label | ... ++ |
| test.cpp:951:3:951:25 | ... = ... | semmle.label | ... = ... |
| test.cpp:951:14:951:25 | ... + ... | semmle.label | ... + ... |
| test.cpp:969:3:969:12 | ... ++ | semmle.label | ... ++ |
| test.cpp:1031:2:1031:11 | ... ++ | semmle.label | ... ++ |
| test.cpp:1038:26:1038:26 | *x | semmle.label | *x |
| test.cpp:1039:2:1039:4 | ... ++ | semmle.label | ... ++ |
| test.cpp:1042:37:1042:37 | *x | semmle.label | *x |
| test.cpp:1043:2:1043:7 | ... ++ | semmle.label | ... ++ |
| test.cpp:1051:16:1051:23 | increment_arg output argument | semmle.label | increment_arg output argument |
| test.cpp:1055:27:1055:35 | increment_arg_by_pointer output argument | semmle.label | increment_arg_by_pointer output argument |
| test.cpp:1109:2:1109:26 | ... = ... | semmle.label | ... = ... |
| test.cpp:1109:14:1109:26 | ... - ... | semmle.label | ... - ... |
| test.cpp:1158:2:1158:15 | ... += ... | semmle.label | ... += ... |
| test.cpp:1160:2:1160:19 | ... = ... | semmle.label | ... = ... |
| test.cpp:1199:2:1199:28 | ... = ... | semmle.label | ... = ... |
| test.cpp:1199:16:1199:28 | ... + ... | semmle.label | ... + ... |
| test.cpp:1214:2:1214:28 | ... = ... | semmle.label | ... = ... |
| test.cpp:1214:16:1214:28 | ... + ... | semmle.label | ... + ... |
| test.cpp:1228:2:1228:28 | ... = ... | semmle.label | ... = ... |
| test.cpp:1228:16:1228:28 | ... + ... | semmle.label | ... + ... |
| test.cpp:1242:2:1242:26 | ... = ... | semmle.label | ... = ... |
| test.cpp:1242:14:1242:26 | ... + ... | semmle.label | ... + ... |
| test.cpp:1256:2:1256:26 | ... = ... | semmle.label | ... = ... |
| test.cpp:1256:14:1256:26 | ... + ... | semmle.label | ... + ... |
| test.cpp:1262:2:1262:28 | ... = ... | semmle.label | ... = ... |
| test.cpp:1262:16:1262:28 | ... + ... | semmle.label | ... + ... |
| test.cpp:1274:2:1274:28 | ... = ... | semmle.label | ... = ... |
| test.cpp:1274:16:1274:28 | ... + ... | semmle.label | ... + ... |
| test.cpp:1287:2:1287:26 | ... = ... | semmle.label | ... = ... |
| test.cpp:1287:14:1287:26 | ... + ... | semmle.label | ... + ... |
| test.cpp:1299:2:1299:26 | ... = ... | semmle.label | ... = ... |
| test.cpp:1299:14:1299:26 | ... + ... | semmle.label | ... + ... |
| test.cpp:1338:20:1338:23 | year | semmle.label | year |
| test.cpp:1341:2:1341:17 | ... = ... | semmle.label | ... = ... |
| test.cpp:1351:3:1351:22 | ... = ... | semmle.label | ... = ... |
| test.cpp:1351:15:1351:22 | ... + ... | semmle.label | ... + ... |
| test.cpp:1356:12:1356:17 | ... + ... | semmle.label | ... + ... |
| test.cpp:1365:3:1365:22 | ... = ... | semmle.label | ... = ... |
| test.cpp:1365:15:1365:22 | ... + ... | semmle.label | ... + ... |
| test.cpp:1375:3:1375:20 | ... = ... | semmle.label | ... = ... |
| test.cpp:1375:13:1375:20 | ... + ... | semmle.label | ... + ... |
| test.cpp:1377:12:1377:18 | yeartmp | semmle.label | yeartmp |
| test.cpp:1420:3:1420:22 | ... = ... | semmle.label | ... = ... |
| test.cpp:1420:15:1420:22 | ... + ... | semmle.label | ... + ... |
| test.cpp:1425:12:1425:17 | ... + ... | semmle.label | ... + ... |
| test.cpp:1432:12:1432:17 | ... + ... | semmle.label | ... + ... |
| test.cpp:1446:2:1446:16 | ... = ... | semmle.label | ... = ... |
| test.cpp:1446:9:1446:16 | ... + ... | semmle.label | ... + ... |
| test.cpp:1450:3:1450:18 | ... = ... | semmle.label | ... = ... |
| test.cpp:1455:12:1455:15 | year | semmle.label | year |
| test.cpp:1458:2:1458:16 | ... = ... | semmle.label | ... = ... |
| test.cpp:1458:9:1458:16 | ... + ... | semmle.label | ... + ... |
| test.cpp:1464:12:1464:15 | year | semmle.label | year |
| test.cpp:1512:2:1512:10 | ... += ... | semmle.label | ... += ... |
| test.cpp:1515:2:1515:15 | ... = ... | semmle.label | ... = ... |
| test.cpp:1545:2:1545:22 | ... += ... | semmle.label | ... += ... |
| test.cpp:1553:2:1553:22 | ... += ... | semmle.label | ... += ... |
| test.cpp:1632:2:1632:22 | ... += ... | semmle.label | ... += ... |
| test.cpp:1644:2:1644:22 | ... += ... | semmle.label | ... += ... |
| test.cpp:1677:2:1677:22 | ... += ... | semmle.label | ... += ... |
| test.cpp:1753:2:1753:22 | ... += ... | semmle.label | ... += ... |
subpaths

View File

@@ -1 +1,2 @@
Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification.ql
query: Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,5 +1,6 @@
| test.cpp:317:2:317:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:63:1:63:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:309:13:309:14 | st | st |
| test.cpp:330:2:330:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:63:1:63:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:322:13:322:14 | st | st |
| test.cpp:341:2:341:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:63:1:63:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:333:62:333:63 | st | st |
| test.cpp:720:2:720:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:63:1:63:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:712:13:712:14 | st | st |
| test.cpp:735:2:735:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:63:1:63:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:725:13:725:14 | st | st |
| test.cpp:425:2:425:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:101:1:101:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:417:13:417:14 | st | st |
| test.cpp:443:2:443:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:101:1:101:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:435:13:435:14 | st | st |
| test.cpp:459:2:459:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:101:1:101:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:451:62:451:63 | st | st |
| test.cpp:953:3:953:22 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:101:1:101:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:944:14:944:15 | st | st |
| test.cpp:971:3:971:22 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:101:1:101:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:962:14:962:15 | st | st |
| test.cpp:1035:2:1035:21 | call to SystemTimeToFileTime | Return value of $@ function should be verified to check for any error because variable $@ is not guaranteed to be safe. | test.cpp:101:1:101:20 | SystemTimeToFileTime | SystemTimeToFileTime | test.cpp:1025:13:1025:14 | st | st |

View File

@@ -250,3 +250,8 @@ void* test_strndupa(const char* s, size_t size) {
return s2; // BAD
}
int* f_rec(int *p) {
int x;
int* px = f_rec(&x); // GOOD
return p;
}

View File

@@ -44,5 +44,5 @@ NHibernate,3,,,,,,,,,,,,3,,,,,,,,,,
Newtonsoft.Json,,,91,,,,,,,,,,,,,,,,,,,73,18
ServiceStack,194,,7,27,,,,,75,,,,92,,,,,,,,,7,
SourceGenerators,,,5,,,,,,,,,,,,,,,,,,,,5
System,59,47,12491,,6,5,12,,,4,1,,31,2,,6,15,17,4,3,,6378,6113
System,59,47,12495,,6,5,12,,,4,1,,31,2,,6,15,17,4,3,,6382,6113
Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,,,,,,,,,
1 package sink source summary sink:code-injection sink:encryption-decryptor sink:encryption-encryptor sink:encryption-keyprop sink:encryption-symmetrickey sink:file-content-store sink:html-injection sink:js-injection sink:log-injection sink:sql-injection source:commandargs source:database source:environment source:file source:file-write source:remote source:stdin source:windows-registry summary:taint summary:value
44 Newtonsoft.Json 91 73 18
45 ServiceStack 194 7 27 75 92 7
46 SourceGenerators 5 5
47 System 59 47 12491 12495 6 5 12 4 1 31 2 6 15 17 4 3 6378 6382 6113
48 Windows.Security.Cryptography.Core 1 1

View File

@@ -8,7 +8,7 @@ C# framework & library support
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
System,"``System.*``, ``System``",47,12491,59,5
System,"``System.*``, ``System``",47,12495,59,5
Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Http``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Data.SqlClient``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``NHibernate``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",60,2406,162,4
Totals,,107,14904,415,9
Totals,,107,14908,415,9

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
description: Remove the relation `extension_receiver_type` and remove the `extension_type` type kind.
compatibility: backwards
extension_receiver_type.rel: delete

View File

@@ -5,6 +5,7 @@ using System.Security.Cryptography.X509Certificates;
using Semmle.Util;
using Semmle.Util.Logging;
using Newtonsoft.Json;
using System.Linq;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
@@ -37,7 +38,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// </summary>
internal X509Certificate2? Certificate { get; private set; }
internal static DependabotProxy? GetDependabotProxy(ILogger logger, TemporaryDirectory tempWorkingDirectory)
internal static DependabotProxy? GetDependabotProxy(
ILogger logger, IDiagnosticsWriter diagnosticsWriter, TemporaryDirectory tempWorkingDirectory)
{
// Setting HTTP(S)_PROXY and SSL_CERT_FILE have no effect on Windows or macOS,
// but we would still end up using the Dependabot proxy to check for feed reachability.
@@ -112,6 +114,23 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
// Emit a diagnostic for the discovered private registries, so that it is easy
// for users to see that they were picked up.
if (result.RegistryURLs.Count > 0)
{
diagnosticsWriter.AddEntry(new DiagnosticMessage(
Language.CSharp,
"buildless/analysis-using-private-registries",
severity: DiagnosticMessage.TspSeverity.Note,
visibility: new DiagnosticMessage.TspVisibility(true, true, true),
name: "C# extraction used private package registries",
markdownMessage: string.Format(
"C# was extracted using the following private package registries:\n\n{0}\n",
string.Join("\n", result.RegistryURLs.Select(url => string.Format("- `{0}`", url)))
)
));
}
return result;
}

View File

@@ -106,7 +106,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
return BuildScript.Success;
}).Run(SystemBuildActions.Instance, startCallback, exitCallback);
dependabotProxy = DependabotProxy.GetDependabotProxy(logger, tempWorkingDirectory);
dependabotProxy = DependabotProxy.GetDependabotProxy(logger, diagnosticsWriter, tempWorkingDirectory);
try
{

View File

@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Semmle.Util;
using Semmle.Extraction.CSharp.Entities;
namespace Semmle.Extraction.CSharp
@@ -164,6 +165,7 @@ namespace Semmle.Extraction.CSharp
case TypeKind.Enum:
case TypeKind.Delegate:
case TypeKind.Error:
case TypeKind.Extension:
var named = (INamedTypeSymbol)type;
named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType);
return;
@@ -275,6 +277,20 @@ namespace Semmle.Extraction.CSharp
public static IEnumerable<IFieldSymbol?> GetTupleElementsMaybeNull(this INamedTypeSymbol type) =>
type.TupleElements;
private static void BuildExtensionTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile)
{
trapFile.Write("extension(");
if (named.ExtensionMarkerName is not null)
{
trapFile.Write(named.ExtensionMarkerName);
}
else
{
trapFile.Write("unknown");
}
trapFile.Write(")");
}
private static void BuildQualifierAndName(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined)
{
if (named.ContainingType is not null)
@@ -289,8 +305,18 @@ namespace Semmle.Extraction.CSharp
named.ContainingNamespace.BuildNamespace(cx, trapFile);
}
var name = named.IsFileLocal ? named.MetadataName : named.Name;
trapFile.Write(name);
if (named.IsFileLocal)
{
trapFile.Write(named.MetadataName);
}
else if (named.IsExtension)
{
named.BuildExtensionTypeId(cx, trapFile);
}
else
{
trapFile.Write(named.Name);
}
}
private static void BuildTupleId(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined)
@@ -391,6 +417,7 @@ namespace Semmle.Extraction.CSharp
case TypeKind.Enum:
case TypeKind.Delegate:
case TypeKind.Error:
case TypeKind.Extension:
var named = (INamedTypeSymbol)type;
named.BuildNamedTypeDisplayName(cx, trapFile, constructUnderlyingTupleType);
return;
@@ -465,6 +492,20 @@ namespace Semmle.Extraction.CSharp
private static void BuildFunctionPointerTypeDisplayName(this IFunctionPointerTypeSymbol funptr, Context cx, TextWriter trapFile) =>
BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildDisplayName(cx, trapFile));
private static void BuildExtensionTypeDisplayName(this INamedTypeSymbol named, Context cx, TextWriter trapFile)
{
trapFile.Write("extension(");
if (named.ExtensionParameter?.Type is ITypeSymbol type)
{
type.BuildDisplayName(cx, trapFile);
}
else
{
trapFile.Write("unknown");
}
trapFile.Write(")");
}
private static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType)
{
if (!constructUnderlyingTupleType && namedType.IsTupleType)
@@ -484,6 +525,12 @@ namespace Semmle.Extraction.CSharp
return;
}
if (namedType.IsExtension)
{
namedType.BuildExtensionTypeDisplayName(cx, trapFile);
return;
}
if (namedType.IsAnonymousType)
{
namedType.BuildAnonymousName(cx, trapFile);
@@ -596,6 +643,84 @@ namespace Semmle.Extraction.CSharp
return true;
}
/// <summary>
/// Return true if this method is a compiler-generated extension method.
/// </summary>
public static bool IsCompilerGeneratedExtensionMethod(this IMethodSymbol method) =>
method.TryGetExtensionMethod() is not null;
/// <summary>
/// Returns the extension method corresponding to this compiler-generated extension method, if it exists.
/// </summary>
public static IMethodSymbol? TryGetExtensionMethod(this IMethodSymbol method)
{
if (method.IsImplicitlyDeclared && method.ContainingSymbol is INamedTypeSymbol containingType)
{
// Extension types are declared within the same type as the generated
// extension method implementation.
var extensions = containingType.GetMembers()
.OfType<INamedTypeSymbol>()
.Where(t => t.IsExtension);
// Find the (possibly unbound) original extension method that maps to this implementation (if any).
var unboundDeclaration = extensions.SelectMany(e => e.GetMembers())
.OfType<IMethodSymbol>()
.FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method.ConstructedFrom));
var isFullyConstructed = method.IsBoundGenericMethod();
if (isFullyConstructed && unboundDeclaration?.ContainingType is INamedTypeSymbol extensionType)
{
try
{
// Use the type arguments from the constructed extension method to construct the extension type.
var arguments = method.TypeArguments.ToArray();
var (extensionTypeArguments, extensionMethodArguments) = arguments.SplitAt(extensionType.TypeParameters.Length);
// Construct the extension type.
var boundExtensionType = extensionType.IsUnboundGenericType()
? extensionType.Construct(extensionTypeArguments.ToArray())
: extensionType;
// Find the extension method declaration within the constructed extension type.
var extensionDeclaration = boundExtensionType.GetMembers()
.OfType<IMethodSymbol>()
.First(c => SymbolEqualityComparer.Default.Equals(c.OriginalDefinition, unboundDeclaration));
// If the extension declaration is unbound apply the remaning type arguments and construct it.
return extensionDeclaration.IsUnboundGenericMethod()
? extensionDeclaration.Construct(extensionMethodArguments.ToArray())
: extensionDeclaration;
}
catch
{
// If anything goes wrong, fall back to the unbound declaration.
return unboundDeclaration;
}
}
else
{
return unboundDeclaration;
}
}
return null;
}
/// <summary>
/// Returns true if this method is an unbound generic method.
/// </summary>
public static bool IsUnboundGenericMethod(this IMethodSymbol method) =>
method.IsGenericMethod && SymbolEqualityComparer.Default.Equals(method.ConstructedFrom, method);
/// <summary>
/// Returns true if this method is a bound generic method.
/// </summary>
public static bool IsBoundGenericMethod(this IMethodSymbol method) => method.IsGenericMethod && !method.IsUnboundGenericMethod();
/// <summary>
/// Returns true if this type is an unbound generic type.
/// </summary>
public static bool IsUnboundGenericType(this INamedTypeSymbol type) =>
type.IsGenericType && SymbolEqualityComparer.Default.Equals(type.ConstructedFrom, type);
/// <summary>
/// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base
/// types of type parameters as well as `object` base types.
@@ -603,6 +728,15 @@ namespace Semmle.Extraction.CSharp
public static INamedTypeSymbol? GetNonObjectBaseType(this ITypeSymbol symbol, Context cx) =>
symbol is ITypeParameterSymbol || SymbolEqualityComparer.Default.Equals(symbol.BaseType, cx.Compilation.ObjectType) ? null : symbol.BaseType;
public static IMethodSymbol GetBodyDeclaringSymbol(this IMethodSymbol method) =>
method.PartialImplementationPart ?? method;
public static IPropertySymbol GetBodyDeclaringSymbol(this IPropertySymbol property) =>
property.PartialImplementationPart ?? property;
public static IEventSymbol GetBodyDeclaringSymbol(this IEventSymbol symbol) =>
symbol.PartialImplementationPart ?? symbol;
[return: NotNullIfNotNull(nameof(symbol))]
public static IEntity? CreateEntity(this Context cx, ISymbol symbol)
{
@@ -692,5 +826,35 @@ namespace Semmle.Extraction.CSharp
/// </summary>
public static IEnumerable<T> ExtractionCandidates<T>(this IEnumerable<T> symbols) where T : ISymbol =>
symbols.Where(symbol => symbol.ShouldExtractSymbol());
/// <summary>
/// Returns the parameter kind for this parameter symbol, e.g. `ref`, `out`, `params`, etc.
/// </summary>
public static Parameter.Kind GetParameterKind(this IParameterSymbol parameter)
{
switch (parameter.RefKind)
{
case RefKind.Out:
return Parameter.Kind.Out;
case RefKind.Ref:
return Parameter.Kind.Ref;
case RefKind.In:
return Parameter.Kind.In;
case RefKind.RefReadOnlyParameter:
return Parameter.Kind.RefReadOnly;
default:
if (parameter.IsParams)
return Parameter.Kind.Params;
if (parameter.Ordinal == 0)
{
if (parameter.ContainingSymbol is IMethodSymbol method && method.IsExtensionMethod)
{
return Parameter.Kind.This;
}
}
return Parameter.Kind.None;
}
}
}
}

View File

@@ -70,7 +70,7 @@ namespace Semmle.Extraction.CSharp.Entities
Overrides(trapFile);
if (Symbol.FromSource() && Block is null)
if (Symbol.FromSource() && !HasBody)
{
trapFile.compiler_generated(this);
}

View File

@@ -54,22 +54,6 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
protected static void WriteLocationToTrap<T1>(Action<T1, Location> writeAction, T1 entity, Location l)
{
if (l is not EmptyLocation)
{
writeAction(entity, l);
}
}
protected static void WriteLocationsToTrap<T1>(Action<T1, Location> writeAction, T1 entity, IEnumerable<Location> locations)
{
foreach (var loc in locations)
{
WriteLocationToTrap(writeAction, entity, loc);
}
}
public override bool NeedsPopulation { get; }
public override int GetHashCode() => Symbol is null ? 0 : Symbol.GetHashCode();

View File

@@ -9,9 +9,14 @@ namespace Semmle.Extraction.CSharp.Entities
{
internal abstract class CachedSymbol<T> : CachedEntity<T> where T : class, ISymbol
{
private readonly Lazy<BlockSyntax?> blockLazy;
private readonly Lazy<ExpressionSyntax?> expressionBodyLazy;
protected CachedSymbol(Context cx, T init)
: base(cx, init)
{
blockLazy = new Lazy<BlockSyntax?>(() => GetBlock(Symbol));
expressionBodyLazy = new Lazy<ExpressionSyntax?>(() => GetExpressionBody(Symbol));
}
public virtual Type? ContainingType => Symbol.ContainingType is not null
@@ -32,32 +37,6 @@ namespace Semmle.Extraction.CSharp.Entities
Attribute.ExtractAttributes(Context, Symbol, this);
}
protected void PopulateNullability(TextWriter trapFile, AnnotatedTypeSymbol type)
{
var n = NullabilityEntity.Create(Context, Nullability.Create(type));
if (!type.HasObliviousNullability())
{
trapFile.type_nullability(this, n);
}
}
protected void PopulateRefKind(TextWriter trapFile, RefKind kind)
{
switch (kind)
{
case RefKind.Out:
trapFile.type_annotation(this, Kinds.TypeAnnotation.Out);
break;
case RefKind.Ref:
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
break;
case RefKind.RefReadOnly:
case RefKind.RefReadOnlyParameter:
trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef);
break;
}
}
protected void PopulateScopedKind(TextWriter trapFile, ScopedKind kind)
{
switch (kind)
@@ -113,31 +92,29 @@ namespace Semmle.Extraction.CSharp.Entities
Context.BindComments(this, FullLocation);
}
protected virtual T BodyDeclaringSymbol => Symbol;
public BlockSyntax? Block
private static BlockSyntax? GetBlock(T symbol)
{
get
{
return BodyDeclaringSymbol.DeclaringSyntaxReferences
return symbol.DeclaringSyntaxReferences
.SelectMany(r => r.GetSyntax().ChildNodes())
.OfType<BlockSyntax>()
.FirstOrDefault();
}
}
public ExpressionSyntax? ExpressionBody
private static ExpressionSyntax? GetExpressionBody(T symbol)
{
get
{
return BodyDeclaringSymbol.DeclaringSyntaxReferences
return symbol.DeclaringSyntaxReferences
.SelectMany(r => r.GetSyntax().ChildNodes())
.OfType<ArrowExpressionClauseSyntax>()
.Select(arrow => arrow.Expression)
.FirstOrDefault();
}
}
public BlockSyntax? Block => blockLazy.Value;
public ExpressionSyntax? ExpressionBody => expressionBodyLazy.Value;
public bool HasBody => Block is not null || ExpressionBody is not null;
public virtual bool IsSourceDeclaration => Symbol.IsSourceDeclaration();
public override bool NeedsPopulation => Context.Defines(Symbol);

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.CodeAnalysis;
using Semmle.Extraction.CSharp.Entities;
namespace Semmle.Extraction.CSharp
{
@@ -24,7 +26,7 @@ namespace Semmle.Extraction.CSharp
trapFile.WriteUnescaped('\"');
}
public abstract Location? ReportingLocation { get; }
public abstract Microsoft.CodeAnalysis.Location? ReportingLocation { get; }
public abstract TrapStackBehaviour TrapStackBehaviour { get; }
@@ -65,6 +67,48 @@ namespace Semmle.Extraction.CSharp
}
#endif
protected void PopulateRefKind(TextWriter trapFile, RefKind kind)
{
switch (kind)
{
case RefKind.Out:
trapFile.type_annotation(this, Kinds.TypeAnnotation.Out);
break;
case RefKind.Ref:
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
break;
case RefKind.RefReadOnly:
case RefKind.RefReadOnlyParameter:
trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef);
break;
}
}
protected void PopulateNullability(TextWriter trapFile, AnnotatedTypeSymbol type)
{
var n = NullabilityEntity.Create(Context, Nullability.Create(type));
if (!type.HasObliviousNullability())
{
trapFile.type_nullability(this, n);
}
}
protected static void WriteLocationToTrap<T1>(Action<T1, Entities.Location> writeAction, T1 entity, Entities.Location l)
{
if (l is not EmptyLocation)
{
writeAction(entity, l);
}
}
protected static void WriteLocationsToTrap<T1>(Action<T1, Entities.Location> writeAction, T1 entity, IEnumerable<Entities.Location> locations)
{
foreach (var loc in locations)
{
WriteLocationToTrap(writeAction, entity, loc);
}
}
public override string ToString() => Label.ToString();
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -12,7 +13,9 @@ namespace Semmle.Extraction.CSharp.Entities
internal class Constructor : Method
{
private readonly List<SyntaxNode> declaringReferenceSyntax;
private readonly Lazy<ConstructorDeclarationSyntax?> ordinaryConstructorSyntaxLazy;
private readonly Lazy<TypeDeclarationSyntax?> primaryConstructorSyntaxLazy;
private readonly Lazy<PrimaryConstructorBaseTypeSyntax?> primaryBaseLazy;
private Constructor(Context cx, IMethodSymbol init)
: base(cx, init)
{
@@ -20,8 +23,28 @@ namespace Semmle.Extraction.CSharp.Entities
Symbol.DeclaringSyntaxReferences
.Select(r => r.GetSyntax())
.ToList();
ordinaryConstructorSyntaxLazy = new Lazy<ConstructorDeclarationSyntax?>(() =>
declaringReferenceSyntax
.OfType<ConstructorDeclarationSyntax>()
.FirstOrDefault());
primaryConstructorSyntaxLazy = new Lazy<TypeDeclarationSyntax?>(() =>
declaringReferenceSyntax
.OfType<TypeDeclarationSyntax>()
.FirstOrDefault(t => t is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax));
primaryBaseLazy = new Lazy<PrimaryConstructorBaseTypeSyntax?>(() =>
PrimaryConstructorSyntax?
.BaseList?
.Types
.OfType<PrimaryConstructorBaseTypeSyntax>()
.FirstOrDefault());
}
private ConstructorDeclarationSyntax? OrdinaryConstructorSyntax => ordinaryConstructorSyntaxLazy.Value;
private TypeDeclarationSyntax? PrimaryConstructorSyntax => primaryConstructorSyntaxLazy.Value;
private PrimaryConstructorBaseTypeSyntax? PrimaryBase => primaryBaseLazy.Value;
public override void Populate(TextWriter trapFile)
{
PopulateMethod(trapFile);
@@ -42,7 +65,7 @@ namespace Semmle.Extraction.CSharp.Entities
return;
}
if (MakeSynthetic)
if (MakeSyntheticBody)
{
// Create a synthetic empty body for primary and default constructors.
Statements.SyntheticEmptyBlock.Create(Context, this, 0, Location);
@@ -60,7 +83,7 @@ namespace Semmle.Extraction.CSharp.Entities
// Do not extract initializers for constructed types.
// Extract initializers for constructors with a body, primary constructors
// and default constructors for classes and structs declared in source code.
if (Block is null && ExpressionBody is null && !MakeSynthetic || Context.OnlyScaffold)
if (!HasBody && !MakeSyntheticBody || Context.OnlyScaffold)
{
return;
}
@@ -176,23 +199,6 @@ namespace Semmle.Extraction.CSharp.Entities
init.PopulateArguments(trapFile, arguments, 0);
}
private ConstructorDeclarationSyntax? OrdinaryConstructorSyntax =>
declaringReferenceSyntax
.OfType<ConstructorDeclarationSyntax>()
.FirstOrDefault();
private TypeDeclarationSyntax? PrimaryConstructorSyntax =>
declaringReferenceSyntax
.OfType<TypeDeclarationSyntax>()
.FirstOrDefault(t => t is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax);
private PrimaryConstructorBaseTypeSyntax? PrimaryBase =>
PrimaryConstructorSyntax?
.BaseList?
.Types
.OfType<PrimaryConstructorBaseTypeSyntax>()
.FirstOrDefault();
private bool IsPrimary => PrimaryConstructorSyntax is not null;
// This is a default constructor in a class or struct declared in source.
@@ -211,7 +217,7 @@ namespace Semmle.Extraction.CSharp.Entities
/// </summary>
private bool IsBestSourceLocation => ReportingLocation is not null && Context.IsLocationInContext(ReportingLocation);
private bool MakeSynthetic => (IsPrimary || (IsDefault && IsBestSourceLocation)) && !Context.OnlyScaffold;
private bool MakeSyntheticBody => (IsPrimary || (IsDefault && IsBestSourceLocation)) && !Context.OnlyScaffold;
[return: NotNullIfNotNull(nameof(constructor))]
public static new Constructor? Create(Context cx, IMethodSymbol? constructor)
@@ -223,7 +229,7 @@ namespace Semmle.Extraction.CSharp.Entities
{
case MethodKind.StaticConstructor:
case MethodKind.Constructor:
return ConstructorFactory.Instance.CreateEntityFromSymbol(cx, constructor);
return ConstructorFactory.Instance.CreateEntityFromSymbol(cx, constructor.GetBodyDeclaringSymbol());
default:
throw new InternalError(constructor, "Attempt to create a Constructor from a symbol that isn't a constructor");
}

View File

@@ -30,10 +30,10 @@ namespace Semmle.Extraction.CSharp.Entities
var adder = Symbol.AddMethod;
var remover = Symbol.RemoveMethod;
if (!(adder is null))
if (adder is not null)
Method.Create(Context, adder);
if (!(remover is null))
if (remover is not null)
Method.Create(Context, remover);
PopulateModifiers(trapFile);
@@ -72,7 +72,7 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntityFromSymbol(cx, symbol);
public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntityFromSymbol(cx, symbol.GetBodyDeclaringSymbol());
private class EventFactory : CachedEntityFactory<IEventSymbol, Event>
{

View File

@@ -13,6 +13,10 @@ namespace Semmle.Extraction.CSharp.Entities
this.@event = @event;
}
public override bool NeedsPopulation =>
base.NeedsPopulation &&
!Symbol.IsPartialDefinition; // Accessors always have an implementing declaration as well.
/// <summary>
/// Gets the event symbol associated with accessor `symbol`, or `null`
/// if there is no associated symbol.
@@ -55,7 +59,7 @@ namespace Semmle.Extraction.CSharp.Entities
Overrides(trapFile);
if (Symbol.FromSource() && Block is null)
if (Symbol.FromSource() && !HasBody)
{
trapFile.compiler_generated(this);
}

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