mirror of
https://github.com/github/codeql.git
synced 2026-04-20 14:34:04 +02:00
Merge branch 'github:main' into amammad-cpp-bombs
This commit is contained in:
@@ -1 +1 @@
|
||||
7.1.2
|
||||
7.2.1
|
||||
|
||||
14
.devcontainer/swift/root.sh
Normal file → Executable file
14
.devcontainer/swift/root.sh
Normal file → Executable file
@@ -3,6 +3,16 @@ set -xe
|
||||
BAZELISK_VERSION=v1.12.0
|
||||
BAZELISK_DOWNLOAD_SHA=6b0bcb2ea15bca16fffabe6fda75803440375354c085480fe361d2cbf32501db
|
||||
|
||||
# install git lfs apt source
|
||||
curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash
|
||||
|
||||
# install gh apt source
|
||||
(type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) \
|
||||
&& sudo mkdir -p -m 755 /etc/apt/keyrings \
|
||||
&& wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
|
||||
&& sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
||||
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
|
||||
|
||||
apt-get update
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get -y install --no-install-recommends \
|
||||
@@ -10,7 +20,9 @@ apt-get -y install --no-install-recommends \
|
||||
uuid-dev \
|
||||
python3-distutils \
|
||||
python3-pip \
|
||||
bash-completion
|
||||
bash-completion \
|
||||
git-lfs \
|
||||
gh
|
||||
|
||||
# Install Bazel
|
||||
curl -fSsL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/${BAZELISK_VERSION}/bazelisk-linux-amd64
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
set -xe
|
||||
|
||||
git lfs install
|
||||
|
||||
# add the workspace to the codeql search path
|
||||
mkdir -p /home/vscode/.config/codeql
|
||||
echo "--search-path /workspaces/codeql" > /home/vscode/.config/codeql/config
|
||||
|
||||
2
.github/workflows/ruby-build.yml
vendored
2
.github/workflows/ruby-build.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
- .github/workflows/ruby-build.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
- "shared/tree-sitter-extractor/**"
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
@@ -16,6 +17,7 @@ on:
|
||||
- .github/workflows/ruby-build.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
- "shared/tree-sitter-extractor/**"
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
|
||||
46
MODULE.bazel
46
MODULE.bazel
@@ -13,22 +13,45 @@ local_path_override(
|
||||
|
||||
# see https://registry.bazel.build/ for a list of available packages
|
||||
|
||||
bazel_dep(name = "platforms", version = "0.0.9")
|
||||
bazel_dep(name = "rules_go", version = "0.47.0")
|
||||
bazel_dep(name = "platforms", version = "0.0.10")
|
||||
bazel_dep(name = "rules_go", version = "0.48.0")
|
||||
bazel_dep(name = "rules_pkg", version = "0.10.1")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.0.3")
|
||||
bazel_dep(name = "rules_python", version = "0.31.0")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.5.0")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.2.0-codeql.1")
|
||||
bazel_dep(name = "rules_python", version = "0.32.2")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.6.1")
|
||||
bazel_dep(name = "abseil-cpp", version = "20240116.0", repo_name = "absl")
|
||||
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
|
||||
bazel_dep(name = "fmt", version = "10.0.0")
|
||||
bazel_dep(name = "rules_kotlin", version = "1.9.4-codeql.1")
|
||||
bazel_dep(name = "gazelle", version = "0.36.0")
|
||||
bazel_dep(name = "gazelle", version = "0.37.0")
|
||||
bazel_dep(name = "rules_dotnet", version = "0.15.1")
|
||||
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
|
||||
bazel_dep(name = "rules_rust", version = "0.46.0")
|
||||
|
||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
|
||||
crate = use_extension(
|
||||
"@rules_rust//crate_universe:extension.bzl",
|
||||
"crate",
|
||||
)
|
||||
crate.from_cargo(
|
||||
name = "py_deps",
|
||||
cargo_lockfile = "//python/extractor/tsg-python:Cargo.lock",
|
||||
manifests = [
|
||||
"//python/extractor/tsg-python:Cargo.toml",
|
||||
"//python/extractor/tsg-python/tsp:Cargo.toml",
|
||||
],
|
||||
)
|
||||
crate.from_cargo(
|
||||
name = "ruby_deps",
|
||||
cargo_lockfile = "//ruby/extractor:Cargo.lock",
|
||||
manifests = [
|
||||
"//ruby/extractor:Cargo.toml",
|
||||
"//ruby/extractor/codeql-extractor-fake-crate:Cargo.toml",
|
||||
],
|
||||
)
|
||||
use_repo(crate, "py_deps", "ruby_deps")
|
||||
|
||||
dotnet = use_extension("@rules_dotnet//dotnet:extensions.bzl", "dotnet")
|
||||
dotnet.toolchain(dotnet_version = "8.0.101")
|
||||
use_repo(dotnet, "dotnet_toolchains")
|
||||
@@ -62,6 +85,10 @@ use_repo(
|
||||
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
|
||||
node.toolchain(
|
||||
name = "nodejs",
|
||||
node_urls = [
|
||||
"https://nodejs.org/dist/v{version}/{filename}",
|
||||
"https://mirrors.dotsrc.org/nodejs/release/v{version}/{filename}",
|
||||
],
|
||||
node_version = "18.15.0",
|
||||
)
|
||||
use_repo(node, "nodejs", "nodejs_toolchains")
|
||||
@@ -85,6 +112,7 @@ use_repo(
|
||||
"kotlin-compiler-1.9.0-Beta",
|
||||
"kotlin-compiler-1.9.20-Beta",
|
||||
"kotlin-compiler-2.0.0-RC1",
|
||||
"kotlin-compiler-2.0.20-Beta2",
|
||||
"kotlin-compiler-embeddable-1.5.0",
|
||||
"kotlin-compiler-embeddable-1.5.10",
|
||||
"kotlin-compiler-embeddable-1.5.20",
|
||||
@@ -97,6 +125,7 @@ use_repo(
|
||||
"kotlin-compiler-embeddable-1.9.0-Beta",
|
||||
"kotlin-compiler-embeddable-1.9.20-Beta",
|
||||
"kotlin-compiler-embeddable-2.0.0-RC1",
|
||||
"kotlin-compiler-embeddable-2.0.20-Beta2",
|
||||
"kotlin-stdlib-1.5.0",
|
||||
"kotlin-stdlib-1.5.10",
|
||||
"kotlin-stdlib-1.5.20",
|
||||
@@ -109,11 +138,16 @@ use_repo(
|
||||
"kotlin-stdlib-1.9.0-Beta",
|
||||
"kotlin-stdlib-1.9.20-Beta",
|
||||
"kotlin-stdlib-2.0.0-RC1",
|
||||
"kotlin-stdlib-2.0.20-Beta2",
|
||||
)
|
||||
|
||||
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
|
||||
go_sdk.download(version = "1.22.2")
|
||||
|
||||
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")
|
||||
|
||||
lfs_files = use_repo_rule("//misc/bazel:lfs.bzl", "lfs_files")
|
||||
|
||||
lfs_files(
|
||||
|
||||
@@ -61,10 +61,6 @@
|
||||
"java/ql/src/utils/modelgenerator/internal/CaptureModels.qll",
|
||||
"csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll"
|
||||
],
|
||||
"Model as Data Generation Java/C# - CaptureModelsPrinting": [
|
||||
"java/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll",
|
||||
"csharp/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll"
|
||||
],
|
||||
"Sign Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
|
||||
@@ -185,11 +181,6 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll"
|
||||
],
|
||||
"C++ IR ValueNumberingImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"
|
||||
|
||||
2296
cpp/downgrades/25e365d1e8147df0f759b604f96eb4bffea48271/old.dbscheme
Normal file
2296
cpp/downgrades/25e365d1e8147df0f759b604f96eb4bffea48271/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
description: Revert support for using-enum declarations.
|
||||
compatibility: partial
|
||||
usings.rel: run usings.qlo
|
||||
using_container.rel: run using_container.qlo
|
||||
@@ -0,0 +1,14 @@
|
||||
class UsingEntry extends @using {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from UsingEntry u, Element parent, int kind
|
||||
where
|
||||
usings(u, _, _, kind) and
|
||||
using_container(parent, u) and
|
||||
kind != 3
|
||||
select parent, u
|
||||
@@ -0,0 +1,17 @@
|
||||
class UsingEntry extends @using {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Location extends @location_default {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from UsingEntry u, Element target, Location loc, int kind
|
||||
where
|
||||
usings(u, target, loc, kind) and
|
||||
kind != 3
|
||||
select u, target, loc
|
||||
@@ -0,0 +1,17 @@
|
||||
class Expr extends @expr {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Location extends @location_expr {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
predicate isExprWithNewBuiltin(Expr expr) {
|
||||
exists(int kind | exprs(expr, kind, _) | 364 <= kind and kind <= 384)
|
||||
}
|
||||
|
||||
from Expr expr, int kind, int kind_new, Location location
|
||||
where
|
||||
exprs(expr, kind, location) and
|
||||
if isExprWithNewBuiltin(expr) then kind_new = 1 else kind_new = kind
|
||||
select expr, kind_new, location
|
||||
2289
cpp/downgrades/3d35dd6b50edfc540c14c6757e0c7b3c5b7b04dd/old.dbscheme
Normal file
2289
cpp/downgrades/3d35dd6b50edfc540c14c6757e0c7b3c5b7b04dd/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Add new builtin operations
|
||||
compatibility: partial
|
||||
exprs.rel: run exprs.qlo
|
||||
2300
cpp/downgrades/9629fc87dab7dbed0771bf5ce22bce4d7f943b52/old.dbscheme
Normal file
2300
cpp/downgrades/9629fc87dab7dbed0771bf5ce22bce4d7f943b52/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Support destroying deletes
|
||||
compatibility: full
|
||||
@@ -1,3 +1,37 @@
|
||||
## 1.3.0
|
||||
|
||||
### New Features
|
||||
|
||||
* Models-as-data alert provenance information has been extended to the C/C++ language. Any qltests that include the edges relation in their output (for example, `.qlref`s that reference path-problem queries) will need to be have their expected output updated accordingly.
|
||||
* Added subclasses of `BuiltInOperations` for `__builtin_has_attribute`, `__builtin_is_corresponding_member`, `__builtin_is_pointer_interconvertible_with_class`, `__is_assignable_no_precondition_check`, `__is_bounded_array`, `__is_convertible`, `__is_corresponding_member`, `__is_nothrow_convertible`, `__is_pointer_interconvertible_with_class`, `__is_referenceable`, `__is_same_as`, `__is_trivially_copy_assignable`, `__is_unbounded_array`, `__is_valid_winrt_type`, `_is_win_class`, `__is_win_interface`, `__reference_binds_to_temporary`, `__reference_constructs_from_temporary`, and `__reference_converts_from_temporary`.
|
||||
* The class `NewArrayExpr` adds a predicate `getArraySize()` to allow a more convenient way to access the static size of the array when the extent is missing.
|
||||
|
||||
## 1.2.0
|
||||
|
||||
### New Features
|
||||
|
||||
* The syntax for models-as-data rows has been extended to make it easier to select sources, sinks, and summaries that involve templated functions and classes. Additionally, the syntax has also been extended to make it easier to specify models with arbitrary levels of indirection. See `dataflow/ExternalFlow.qll` for the updated documentation and specification for the model format.
|
||||
* It is now possible to extend the classes `AllocationFunction` and `DeallocationFunction` via data extensions. Extensions of these classes should be added to the `lib/ext/allocation` and `lib/ext/deallocation` directories respectively.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The queries "Potential double free" (`cpp/double-free`) and "Potential use after free" (`cpp/use-after-free`) now produce fewer false positives.
|
||||
* The "Guards" library (`semmle.code.cpp.controlflow.Guards`) now also infers guards from calls to the builtin operation `__builtin_expect`. As a result, some queries may produce fewer false positives.
|
||||
|
||||
## 1.1.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.1.0
|
||||
|
||||
### New Features
|
||||
|
||||
* Data models can now be added with data extensions. In this way source, sink and summary models can be added in extension `.model.yml` files, rather than by writing classes in QL code. New models should be added in the `lib/ext` folder.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* A partial model for the `Boost.Asio` network library has been added. This includes sources, sinks and summaries for certain functions in `Boost.Asio`, such as `read_until` and `write`.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* A partial model for the `Boost.Asio` network library has been added. This includes sources, sinks and summaries for certain functions in `Boost.Asio`, such as `read_until` and `write`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Data models can now be added with data extensions. In this way source, sink and summary models can be added in extension `.model.yml` files, rather than by writing classes in QL code. New models should be added in the `lib/ext` folder.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* A `isDestroyingDeleteDeallocation` predicate was added to the `NewOrNewArrayExpr` and `DeleteOrDeleteArrayExpr` classes to indicate whether the deallocation function is a destroying delete.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* A `UsingEnumDeclarationEntry` class has been added for C++ `using enum` declarations. As part of this, synthesized `UsingDeclarationEntry`s are no longer emitted for individual enumerators of the referenced enumeration.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved performance of alias analysis of large function bodies. In rare cases, alerts that depend on alias analysis of large function bodies may be affected.
|
||||
9
cpp/ql/lib/change-notes/released/1.1.0.md
Normal file
9
cpp/ql/lib/change-notes/released/1.1.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 1.1.0
|
||||
|
||||
### New Features
|
||||
|
||||
* Data models can now be added with data extensions. In this way source, sink and summary models can be added in extension `.model.yml` files, rather than by writing classes in QL code. New models should be added in the `lib/ext` folder.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* A partial model for the `Boost.Asio` network library has been added. This includes sources, sinks and summaries for certain functions in `Boost.Asio`, such as `read_until` and `write`.
|
||||
3
cpp/ql/lib/change-notes/released/1.1.1.md
Normal file
3
cpp/ql/lib/change-notes/released/1.1.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.1.1
|
||||
|
||||
No user-facing changes.
|
||||
11
cpp/ql/lib/change-notes/released/1.2.0.md
Normal file
11
cpp/ql/lib/change-notes/released/1.2.0.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## 1.2.0
|
||||
|
||||
### New Features
|
||||
|
||||
* The syntax for models-as-data rows has been extended to make it easier to select sources, sinks, and summaries that involve templated functions and classes. Additionally, the syntax has also been extended to make it easier to specify models with arbitrary levels of indirection. See `dataflow/ExternalFlow.qll` for the updated documentation and specification for the model format.
|
||||
* It is now possible to extend the classes `AllocationFunction` and `DeallocationFunction` via data extensions. Extensions of these classes should be added to the `lib/ext/allocation` and `lib/ext/deallocation` directories respectively.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The queries "Potential double free" (`cpp/double-free`) and "Potential use after free" (`cpp/use-after-free`) now produce fewer false positives.
|
||||
* The "Guards" library (`semmle.code.cpp.controlflow.Guards`) now also infers guards from calls to the builtin operation `__builtin_expect`. As a result, some queries may produce fewer false positives.
|
||||
7
cpp/ql/lib/change-notes/released/1.3.0.md
Normal file
7
cpp/ql/lib/change-notes/released/1.3.0.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## 1.3.0
|
||||
|
||||
### New Features
|
||||
|
||||
* Models-as-data alert provenance information has been extended to the C/C++ language. Any qltests that include the edges relation in their output (for example, `.qlref`s that reference path-problem queries) will need to be have their expected output updated accordingly.
|
||||
* Added subclasses of `BuiltInOperations` for `__builtin_has_attribute`, `__builtin_is_corresponding_member`, `__builtin_is_pointer_interconvertible_with_class`, `__is_assignable_no_precondition_check`, `__is_bounded_array`, `__is_convertible`, `__is_corresponding_member`, `__is_nothrow_convertible`, `__is_pointer_interconvertible_with_class`, `__is_referenceable`, `__is_same_as`, `__is_trivially_copy_assignable`, `__is_unbounded_array`, `__is_valid_winrt_type`, `_is_win_class`, `__is_win_interface`, `__reference_binds_to_temporary`, `__reference_constructs_from_temporary`, and `__reference_converts_from_temporary`.
|
||||
* The class `NewArrayExpr` adds a predicate `getArraySize()` to allow a more convenient way to access the static size of the array when the extent is missing.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.0.0
|
||||
lastReleaseVersion: 1.3.0
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
extensions:
|
||||
# partial model of the Boost::Asio network library
|
||||
extensions:
|
||||
- addsTo:
|
||||
|
||||
7
cpp/ql/lib/ext/allocation/Bsd.allocation.model.yml
Normal file
7
cpp/ql/lib/ext/allocation/Bsd.allocation.model.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: allocationFunctionModel
|
||||
data:
|
||||
- ["", "", False, "kmem_alloc", "0", "", "", True]
|
||||
- ["", "", False, "kmem_zalloc", "0", "", "", True]
|
||||
7
cpp/ql/lib/ext/allocation/Glibc.allocation.model.yml
Normal file
7
cpp/ql/lib/ext/allocation/Glibc.allocation.model.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: allocationFunctionModel
|
||||
data:
|
||||
- ["", "", False, "g_malloc", "0", "", "", True]
|
||||
- ["", "", False, "g_try_malloc", "0", "", "", True]
|
||||
10
cpp/ql/lib/ext/allocation/OpenSSL.allocation.model.yml
Normal file
10
cpp/ql/lib/ext/allocation/OpenSSL.allocation.model.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: allocationFunctionModel
|
||||
data:
|
||||
- ["", "", False, "CRYPTO_malloc", "0", "", "", True]
|
||||
- ["", "", False, "CRYPTO_zalloc", "0", "", "", True]
|
||||
- ["", "", False, "CRYPTO_secure_malloc", "0", "", "", True]
|
||||
- ["", "", False, "CRYPTO_secure_zalloc", "0", "", "", True]
|
||||
|
||||
15
cpp/ql/lib/ext/allocation/Std.allocation.model.yml
Normal file
15
cpp/ql/lib/ext/allocation/Std.allocation.model.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: allocationFunctionModel
|
||||
data:
|
||||
- ["", "", False, "malloc", "0", "", "", True]
|
||||
- ["std", "", False, "malloc", "0", "", "", True]
|
||||
- ["bsl", "", False, "malloc", "0", "", "", True]
|
||||
- ["", "", False, "alloca", "0", "", "", False]
|
||||
- ["", "", False, "__builtin_alloca", "0", "", "", False]
|
||||
- ["", "", False, "_alloca", "0", "", "", False]
|
||||
- ["", "", False, "_malloca", "0", "", "", False]
|
||||
- ["", "", False, "calloc", "1", "0", "", True]
|
||||
- ["std", "", False, "calloc", "1", "0", "", True]
|
||||
- ["bsl", "", False, "calloc", "1", "0", "", True]
|
||||
29
cpp/ql/lib/ext/allocation/Windows.allocation.model.yml
Normal file
29
cpp/ql/lib/ext/allocation/Windows.allocation.model.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: allocationFunctionModel
|
||||
data:
|
||||
- ["", "", False, "MmAllocateContiguousMemory", "0", "", "", True]
|
||||
- ["", "", False, "MmAllocateContiguousNodeMemory", "0", "", "", True]
|
||||
- ["", "", False, "MmAllocateContiguousMemorySpecifyCache", "0", "", "", True]
|
||||
- ["", "", False, "MmAllocateContiguousMemorySpecifyCacheNode", "0", "", "", True]
|
||||
- ["", "", False, "MmAllocateNonCachedMemory", "0", "", "", True]
|
||||
- ["", "", False, "MmAllocateMappingAddress", "0", "", "", True]
|
||||
- ["", "", False, "CoTaskMemAlloc", "0", "", "", True]
|
||||
- ["", "", False, "ExAllocatePool", "1", "", "", True]
|
||||
- ["", "", False, "ExAllocatePool2", "1", "", "", True]
|
||||
- ["", "", False, "ExAllocatePool3", "1", "", "", True]
|
||||
- ["", "", False, "ExAllocatePoolWithTag", "1", "", "", True]
|
||||
- ["", "", False, "ExAllocatePoolWithTagPriority", "1", "", "", True]
|
||||
- ["", "", False, "ExAllocatePoolWithQuota", "1", "", "", True]
|
||||
- ["", "", False, "ExAllocatePoolWithQuotaTag", "1", "", "", True]
|
||||
- ["", "", False, "ExAllocatePoolZero", "1", "", "", True]
|
||||
- ["", "", False, "IoAllocateMdl", "1", "", "", True]
|
||||
- ["", "", False, "IoAllocateErrorLogEntry", "1", "", "", True]
|
||||
- ["", "", False, "LocalAlloc", "1", "", "", True]
|
||||
- ["", "", False, "GlobalAlloc", "1", "", "", True]
|
||||
- ["", "", False, "VirtualAlloc", "1", "", "", True]
|
||||
- ["", "", False, "HeapAlloc", "2", "", "", True]
|
||||
- ["", "", False, "MmAllocatePagesForMdl", "3", "", "", True]
|
||||
- ["", "", False, "MmAllocatePagesForMdlEx", "3", "", "", True]
|
||||
- ["", "", False, "MmAllocateNodePagesForMdlEx", "3", "", "", True]
|
||||
5
cpp/ql/lib/ext/allocation/empty.allocation.model.yml
Normal file
5
cpp/ql/lib/ext/allocation/empty.allocation.model.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: allocationFunctionModel
|
||||
data: []
|
||||
14
cpp/ql/lib/ext/bsl.array.model.yml
Normal file
14
cpp/ql/lib/ext/bsl.array.model.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["bsl", "array", True, "at", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "array", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "array", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "array", True, "data", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "array", True, "operator[]", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "array", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "array", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "array", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "array", True, "back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
73
cpp/ql/lib/ext/bsl.deque.model.yml
Normal file
73
cpp/ql/lib/ext/bsl.deque.model.yml
Normal file
@@ -0,0 +1,73 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["bsl", "deque<T,Allocator>", True, "assign", "(size_type,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "assign<InputIt>", "(InputIt,InputIt)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "at", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "deque", "(const deque &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "deque", "(deque &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@3]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@4]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[*@5]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "emplace_front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "operator=", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "operator[]", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "push_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "push_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T,Allocator>", True, "deque", "(const deque &,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T,Allocator>", True, "deque", "(deque &&,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T,Allocator>", True, "deque", "(size_type,const T &,const Allocator &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T,Allocator>", True, "deque<InputIterator>", "(InputIterator,InputIterator,const Allocator &)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "deque<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
56
cpp/ql/lib/ext/bsl.forward_list.model.yml
Normal file
56
cpp/ql/lib/ext/bsl.forward_list.model.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["bsl", "forward_list", True, "insert_after<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,size_type,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T,Allocator>", True, "assign", "(size_type,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "assign<InputIt>", "(InputIt,InputIt)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@3]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@4]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[*@5]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_after", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "emplace_front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "forward_list", "(const forward_list &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "forward_list", "(forward_list &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "insert_after<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "insert_after<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "operator=", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "push_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T,Allocator>", True, "forward_list", "(const forward_list &,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T,Allocator>", True, "forward_list", "(forward_list &&,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T,Allocator>", True, "forward_list", "(InputIterator,InputIterator,const Allocator &)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T,Allocator>", True, "forward_list", "(size_type,const T &,const Allocator &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,const T &)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,T &&)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,T &&)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "forward_list<T>", True, "insert_after", "(const_iterator,T &&)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
71
cpp/ql/lib/ext/bsl.list.model.yml
Normal file
71
cpp/ql/lib/ext/bsl.list.model.yml
Normal file
@@ -0,0 +1,71 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["bsl", "list<T,Allocator>", True, "assign", "(size_type,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "assign<InputIt>", "(InputIt,InputIt)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "emplace_front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "list", "(const list &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "list", "(list &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "operator=", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "push_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "push_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T,Allocator>", True, "list", "(const list &,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T,Allocator>", True, "list", "(list &&,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T,Allocator>", True, "list", "(size_type,const T &,const Allocator &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T,Allocator>", True, "list<InputIterator>", "(InputIterator,InputIterator,const Allocator &)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "list<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
60
cpp/ql/lib/ext/bsl.vector.model.yml
Normal file
60
cpp/ql/lib/ext/bsl.vector.model.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["bsl", "vector<T,Allocator>", True, "assign", "(size_type,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "assign<InputIt>", "(InputIt,InputIt)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "at", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "data", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@3]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@4]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[*@5]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "emplace_back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "operator=", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "operator[]", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "push_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "vector", "(const vector &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector", True, "vector", "(vector &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T,Allocator>", True, "vector", "(const vector &,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T,Allocator>", True, "vector", "(size_type,const T &,const Allocator &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T,Allocator>", True, "vector", "(vector &&,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T,Allocator>", True, "vector<InputIterator>", "(InputIterator,InputIterator,const Allocator &)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["bsl", "vector<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
8
cpp/ql/lib/ext/deallocation/Bsd.deallocation.model.yml
Normal file
8
cpp/ql/lib/ext/deallocation/Bsd.deallocation.model.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: deallocationFunctionModel
|
||||
data:
|
||||
- ["", "", False, "pool_put", "1"]
|
||||
- ["", "", False, "pool_cache_put", "1"]
|
||||
- ["", "", False, "kmem_free", "0"]
|
||||
42
cpp/ql/lib/ext/deallocation/Std.deallocation.model.yml
Normal file
42
cpp/ql/lib/ext/deallocation/Std.deallocation.model.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: deallocationFunctionModel
|
||||
data:
|
||||
- ["", "", False, "free", "0"]
|
||||
- ["std", "", False, "free", "0"]
|
||||
- ["bsl", "", False, "free", "0"]
|
||||
- ["", "", False, "realloc", "0"]
|
||||
- ["std", "", False, "realloc", "0"]
|
||||
- ["bsl", "", False, "realloc", "0"]
|
||||
- ["", "", False, "CRYPTO_free", "0"]
|
||||
- ["", "", False, "CRYPTO_secure_free", "0"]
|
||||
- ["", "", False, "g_free", "0"]
|
||||
- ["", "", False, "ExFreePool", "0"]
|
||||
- ["", "", False, "ExFreePoolWithTag", "0"]
|
||||
- ["", "", False, "ExDeleteTimer", "0"]
|
||||
- ["", "", False, "IoFreeIrp", "0"]
|
||||
- ["", "", False, "IoFreeMdl", "0"]
|
||||
- ["", "", False, "IoFreeErrorLogEntry", "0"]
|
||||
- ["", "", False, "IoFreeWorkItem", "0"]
|
||||
- ["", "", False, "MmFreeContiguousMemory", "0"]
|
||||
- ["", "", False, "MmFreeContiguousMemorySpecifyCache", "0"]
|
||||
- ["", "", False, "MmFreeNonCachedMemory", "0"]
|
||||
- ["", "", False, "MmFreeMappingAddress", "0"]
|
||||
- ["", "", False, "MmFreePagesFromMdl", "0"]
|
||||
- ["", "", False, "MmUnmapReservedMapping", "0"]
|
||||
- ["", "", False, "MmUnmapLockedPages", "0"]
|
||||
- ["", "", False, "NdisFreeGenericObject", "0"]
|
||||
- ["", "", False, "NdisFreeMemory", "0"]
|
||||
- ["", "", False, "NdisFreeMemoryWithTag", "0"]
|
||||
- ["", "", False, "NdisFreeMdl", "0"]
|
||||
- ["", "", False, "NdisFreeNetBufferListPool", "0"]
|
||||
- ["", "", False, "NdisFreeNetBufferPool", "0"]
|
||||
- ["", "", False, "LocalFree", "0"]
|
||||
- ["", "", False, "GlobalFree", "0"]
|
||||
- ["", "", False, "LocalReAlloc", "0"]
|
||||
- ["", "", False, "GlobalReAlloc", "0"]
|
||||
- ["", "", False, "VirtualFree", "0"]
|
||||
- ["", "", False, "CoTaskMemFree", "0"]
|
||||
- ["", "", False, "CoTaskMemRealloc", "0"]
|
||||
- ["", "", False, "SysFreeString", "0"]
|
||||
41
cpp/ql/lib/ext/deallocation/Windows.deallocation.model.yml
Normal file
41
cpp/ql/lib/ext/deallocation/Windows.deallocation.model.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: deallocationFunctionModel
|
||||
data:
|
||||
- ["", "", False, "ExFreePool", "0"]
|
||||
- ["", "", False, "ExFreePoolWithTag", "0"]
|
||||
- ["", "", False, "ExDeleteTimer", "0"]
|
||||
- ["", "", False, "IoFreeIrp", "0"]
|
||||
- ["", "", False, "IoFreeMdl", "0"]
|
||||
- ["", "", False, "IoFreeErrorLogEntry", "0"]
|
||||
- ["", "", False, "IoFreeWorkItem", "0"]
|
||||
- ["", "", False, "MmFreeContiguousMemory", "0"]
|
||||
- ["", "", False, "MmFreeContiguousMemorySpecifyCache", "0"]
|
||||
- ["", "", False, "MmFreeNonCachedMemory", "0"]
|
||||
- ["", "", False, "MmFreeMappingAddress", "0"]
|
||||
- ["", "", False, "MmFreePagesFromMdl", "0"]
|
||||
- ["", "", False, "MmUnmapReservedMapping", "0"]
|
||||
- ["", "", False, "MmUnmapLockedPages", "0"]
|
||||
- ["", "", False, "NdisFreeGenericObject", "0"]
|
||||
- ["", "", False, "NdisFreeMemory", "0"]
|
||||
- ["", "", False, "NdisFreeMemoryWithTag", "0"]
|
||||
- ["", "", False, "NdisFreeMdl", "0"]
|
||||
- ["", "", False, "NdisFreeNetBufferListPool", "0"]
|
||||
- ["", "", False, "NdisFreeNetBufferPool", "0"]
|
||||
- ["", "", False, "LocalFree", "0"]
|
||||
- ["", "", False, "GlobalFree", "0"]
|
||||
- ["", "", False, "LocalReAlloc", "0"]
|
||||
- ["", "", False, "GlobalReAlloc", "0"]
|
||||
- ["", "", False, "VirtualFree", "0"]
|
||||
- ["", "", False, "CoTaskMemFree", "0"]
|
||||
- ["", "", False, "CoTaskMemRealloc", "0"]
|
||||
- ["", "", False, "SysFreeString", "0"]
|
||||
- ["", "", False, "ExFreeToLookasideListEx", "1"]
|
||||
- ["", "", False, "ExFreeToPagedLookasideList", "1"]
|
||||
- ["", "", False, "ExFreeToNPagedLookasideList", "1"]
|
||||
- ["", "", False, "NdisFreeMemoryWithTagPriority", "1"]
|
||||
- ["", "", False, "StorPortFreeMdl", "1"]
|
||||
- ["", "", False, "StorPortFreePool", "1"]
|
||||
- ["", "", False, "HeapFree", "2"]
|
||||
- ["", "", False, "HeapReAlloc", "2"]
|
||||
5
cpp/ql/lib/ext/deallocation/empty.deallocation.model.yml
Normal file
5
cpp/ql/lib/ext/deallocation/empty.deallocation.model.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: deallocationFunctionModel
|
||||
data: []
|
||||
14
cpp/ql/lib/ext/std.array.model.yml
Normal file
14
cpp/ql/lib/ext/std.array.model.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["std", "array", True, "at", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "array", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "array", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "array", True, "data", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "array", True, "operator[]", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "array", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "array", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "array", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "array", True, "back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
73
cpp/ql/lib/ext/std.deque.model.yml
Normal file
73
cpp/ql/lib/ext/std.deque.model.yml
Normal file
@@ -0,0 +1,73 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["std", "deque<T,Allocator>", True, "assign", "(size_type,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "assign<InputIt>", "(InputIt,InputIt)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "at", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "deque", "(const deque &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "deque", "(deque &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@3]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@4]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[*@5]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "emplace_front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "operator=", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "operator[]", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "deque", True, "push_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "push_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T,Allocator>", True, "deque", "(const deque &,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T,Allocator>", True, "deque", "(deque &&,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T,Allocator>", True, "deque", "(size_type,const T &,const Allocator &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T,Allocator>", True, "deque<InputIterator>", "(InputIterator,InputIterator,const Allocator &)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "deque<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
56
cpp/ql/lib/ext/std.forward_list.model.yml
Normal file
56
cpp/ql/lib/ext/std.forward_list.model.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["std", "forward_list", True, "insert_after<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,size_type,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T,Allocator>", True, "assign", "(size_type,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "assign<InputIt>", "(InputIt,InputIt)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@3]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@4]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[*@5]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_after", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "emplace_front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "forward_list", "(const forward_list &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "forward_list", "(forward_list &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "insert_after<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "insert_after<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "operator=", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "push_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T,Allocator>", True, "forward_list", "(const forward_list &,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T,Allocator>", True, "forward_list", "(forward_list &&,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T,Allocator>", True, "forward_list", "(InputIterator,InputIterator,const Allocator &)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T,Allocator>", True, "forward_list", "(size_type,const T &,const Allocator &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,const T &)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,T &&)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,T &&)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "forward_list<T>", True, "insert_after", "(const_iterator,T &&)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
11
cpp/ql/lib/ext/std.iterator.model.yml
Normal file
11
cpp/ql/lib/ext/std.iterator.model.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["std", "iterator", True, "operator*", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "iterator", True, "operator->", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "iterator", True, "iterator", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["__gnu_cxx", "__normal_iterator", True, "operator*", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["__gnu_cxx", "__normal_iterator", True, "operator->", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["__gnu_cxx", "__normal_iterator", True, "__normal_iterator", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
71
cpp/ql/lib/ext/std.list.model.yml
Normal file
71
cpp/ql/lib/ext/std.list.model.yml
Normal file
@@ -0,0 +1,71 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["std", "list<T,Allocator>", True, "assign", "(size_type,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "assign<InputIt>", "(InputIt,InputIt)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@3]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@4]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[*@5]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "emplace_front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "list", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "list", "(const list &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "list", "(list &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "operator=", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "push_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "push_front", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list<T,Allocator>", True, "list", "(const list &,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list<T,Allocator>", True, "list", "(list &&,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list<T,Allocator>", True, "list", "(size_type,const T &,const Allocator &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list<T,Allocator>", True, "list<InputIterator>", "(InputIterator,InputIterator,const Allocator &)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "list<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
60
cpp/ql/lib/ext/std.vector.model.yml
Normal file
60
cpp/ql/lib/ext/std.vector.model.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["std", "vector<T,Allocator>", True, "assign", "(size_type,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "assign<InputIt>", "(InputIt,InputIt)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "at", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "begin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "cbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "data", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@3]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@4]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[*@5]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@0]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@1]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@2]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@3]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@3]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@4]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@4]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@5]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[*@5]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "emplace_back", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "front", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "insert<InputIt>", "(const_iterator,InputIt,InputIt)", "", "Argument[1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "operator=", "", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "operator[]", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
|
||||
- ["std", "vector", True, "push_back", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "rbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "rcbegin", "", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "vector", "(const vector &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector", True, "vector", "(vector &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T,Allocator>", True, "vector", "(const vector &,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T,Allocator>", True, "vector", "(size_type,const T &,const Allocator &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T,Allocator>", True, "vector", "(vector &&,const Allocator &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T,Allocator>", True, "vector<InputIterator>", "(InputIterator,InputIterator,const Allocator &)", "", "Argument[0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[*@2]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,size_type,const T &)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[*@1]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
- ["std", "vector<T>", True, "insert", "(const_iterator,T &&)", "", "Argument[-1].Element[@]", "ReturnValue.Element[@]", "value", "manual"]
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 1.0.1-dev
|
||||
version: 1.3.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
@@ -16,4 +16,6 @@ dependencies:
|
||||
codeql/xml: ${workspace}
|
||||
dataExtensions:
|
||||
- ext/*.model.yml
|
||||
- ext/deallocation/*.model.yml
|
||||
- ext/allocation/*.model.yml
|
||||
warnOnImplicitThis: true
|
||||
|
||||
@@ -156,7 +156,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
|
||||
* A C++ `using` directive or `using` declaration.
|
||||
*/
|
||||
class UsingEntry extends Locatable, @using {
|
||||
override Location getLocation() { usings(underlyingElement(this), _, result) }
|
||||
override Location getLocation() { usings(underlyingElement(this), _, result, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,15 +166,13 @@ class UsingEntry extends Locatable, @using {
|
||||
* ```
|
||||
*/
|
||||
class UsingDeclarationEntry extends UsingEntry {
|
||||
UsingDeclarationEntry() {
|
||||
not exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
|
||||
}
|
||||
UsingDeclarationEntry() { usings(underlyingElement(this), _, _, 1) }
|
||||
|
||||
/**
|
||||
* Gets the declaration that is referenced by this using declaration. For
|
||||
* example, `std::string` in `using std::string`.
|
||||
*/
|
||||
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) }
|
||||
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _, _) }
|
||||
|
||||
override string toString() { result = "using " + this.getDeclaration().getDescription() }
|
||||
}
|
||||
@@ -186,19 +184,36 @@ class UsingDeclarationEntry extends UsingEntry {
|
||||
* ```
|
||||
*/
|
||||
class UsingDirectiveEntry extends UsingEntry {
|
||||
UsingDirectiveEntry() {
|
||||
exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
|
||||
}
|
||||
UsingDirectiveEntry() { usings(underlyingElement(this), _, _, 2) }
|
||||
|
||||
/**
|
||||
* Gets the namespace that is referenced by this using directive. For
|
||||
* example, `std` in `using namespace std`.
|
||||
*/
|
||||
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) }
|
||||
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _, _) }
|
||||
|
||||
override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `using enum` declaration. For example:
|
||||
* ```
|
||||
* enum class Foo { a, b };
|
||||
* using enum Foo;
|
||||
* ```
|
||||
*/
|
||||
class UsingEnumDeclarationEntry extends UsingEntry {
|
||||
UsingEnumDeclarationEntry() { usings(underlyingElement(this), _, _, 3) }
|
||||
|
||||
/**
|
||||
* Gets the enumeration that is referenced by this using directive. For
|
||||
* example, `Foo` in `using enum Foo`.
|
||||
*/
|
||||
Enum getEnum() { usings(underlyingElement(this), unresolveElement(result), _, _) }
|
||||
|
||||
override string toString() { result = "using enum " + this.getEnum().getQualifiedName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `g` is an instance of `GlobalNamespace`. This predicate
|
||||
* is used suppress a warning in `GlobalNamespace.getADeclaration()`
|
||||
|
||||
@@ -410,6 +410,10 @@ class LocalVariable extends LocalScopeVariable, @localvariable {
|
||||
or
|
||||
orphaned_variables(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
override predicate isStatic() {
|
||||
super.isStatic() or orphaned_variables(underlyingElement(this), _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,8 +59,7 @@ class MatchValue extends AbstractValue, TMatchValue {
|
||||
}
|
||||
|
||||
/**
|
||||
* A Boolean condition in the AST that guards one or more basic blocks. This includes
|
||||
* operands of logical operators but not switch statements.
|
||||
* A Boolean condition in the AST that guards one or more basic blocks.
|
||||
*/
|
||||
cached
|
||||
class GuardCondition extends Expr {
|
||||
@@ -366,15 +365,42 @@ private predicate nonExcludedIRAndBasicBlock(IRBlock irb, BasicBlock controlled)
|
||||
}
|
||||
|
||||
/**
|
||||
* A Boolean condition in the IR that guards one or more basic blocks. This includes
|
||||
* operands of logical operators but not switch statements. Note that `&&` and `||`
|
||||
* don't have an explicit representation in the IR, and therefore will not appear as
|
||||
* IRGuardConditions.
|
||||
* A Boolean condition in the IR that guards one or more basic blocks.
|
||||
*
|
||||
* Note that `&&` and `||` don't have an explicit representation in the IR,
|
||||
* and therefore will not appear as IRGuardConditions.
|
||||
*/
|
||||
cached
|
||||
class IRGuardCondition extends Instruction {
|
||||
Instruction branch;
|
||||
|
||||
/*
|
||||
* An `IRGuardCondition` supports reasoning about four different kinds of
|
||||
* relations:
|
||||
* 1. A unary equality relation of the form `e == k`
|
||||
* 2. A binary equality relation of the form `e1 == e2 + k`
|
||||
* 3. A unary inequality relation of the form `e < k`
|
||||
* 4. A binary inequality relation of the form `e1 < e2 + k`
|
||||
*
|
||||
* where `k` is a constant.
|
||||
*
|
||||
* Furthermore, the unary relations (i.e., case 1 and case 3) are also
|
||||
* inferred from `switch` statement guards: equality relations are inferred
|
||||
* from the unique `case` statement, if any, and inequality relations are
|
||||
* inferred from the [case range](https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html)
|
||||
* gcc extension.
|
||||
*
|
||||
* The implementation of all four follows the same structure: Each relation
|
||||
* has a cached user-facing predicate that. For example,
|
||||
* `GuardCondition::comparesEq` calls `compares_eq`. This predicate has
|
||||
* several cases that recursively decompose the relation to bring it to a
|
||||
* canonical form (i.e., a relation of the form `e1 == e2 + k`). The base
|
||||
* case for this relation (i.e., `simple_comparison_eq`) handles
|
||||
* `CompareEQInstruction`s and `CompareNEInstruction`, and recursive
|
||||
* predicates (e.g., `complex_eq`) rewrites larger expressions such as
|
||||
* `e1 + k1 == e2 + k2` into canonical the form `e1 == e2 + (k2 - k1)`.
|
||||
*/
|
||||
|
||||
cached
|
||||
IRGuardCondition() { branch = getBranchForCondition(this) }
|
||||
|
||||
@@ -735,6 +761,8 @@ private predicate compares_eq(
|
||||
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||
compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, dual)
|
||||
)
|
||||
or
|
||||
compares_eq(test.(BuiltinExpectCallInstruction).getCondition(), left, right, k, areEqual, value)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -776,7 +804,9 @@ private predicate unary_compares_eq(
|
||||
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
|
||||
) {
|
||||
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
||||
exists(AbstractValue v | unary_simple_comparison_eq(test, op, k, inNonZeroCase, v) |
|
||||
exists(AbstractValue v |
|
||||
unary_simple_comparison_eq(test, k, inNonZeroCase, v) and op.getDef() = test
|
||||
|
|
||||
areEqual = true and value = v
|
||||
or
|
||||
areEqual = false and value = v.getDualValue()
|
||||
@@ -802,6 +832,9 @@ private predicate unary_compares_eq(
|
||||
int_value(const) = k1 and
|
||||
k = k1 + k2
|
||||
)
|
||||
or
|
||||
unary_compares_eq(test.(BuiltinExpectCallInstruction).getCondition(), op, k, areEqual,
|
||||
inNonZeroCase, value)
|
||||
}
|
||||
|
||||
/** Rearrange various simple comparisons into `left == right + k` form. */
|
||||
@@ -821,45 +854,55 @@ private predicate simple_comparison_eq(
|
||||
value.(BooleanValue).getValue() = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `test` is an instruction that is part of test that eventually is
|
||||
* used in a conditional branch.
|
||||
*/
|
||||
private predicate relevantUnaryComparison(Instruction test) {
|
||||
not test instanceof CompareInstruction and
|
||||
exists(IRType type, ConditionalBranchInstruction branch |
|
||||
type instanceof IRAddressType or type instanceof IRIntegerType
|
||||
|
|
||||
type = test.getResultIRType() and
|
||||
branch.getCondition() = test
|
||||
)
|
||||
or
|
||||
exists(LogicalNotInstruction logicalNot |
|
||||
relevantUnaryComparison(logicalNot) and
|
||||
test = logicalNot.getUnary()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rearrange various simple comparisons into `op == k` form.
|
||||
*/
|
||||
private predicate unary_simple_comparison_eq(
|
||||
Instruction test, Operand op, int k, boolean inNonZeroCase, AbstractValue value
|
||||
Instruction test, int k, boolean inNonZeroCase, AbstractValue value
|
||||
) {
|
||||
exists(SwitchInstruction switch, CaseEdge case |
|
||||
test = switch.getExpression() and
|
||||
op.getDef() = test and
|
||||
case = value.(MatchValue).getCase() and
|
||||
exists(switch.getSuccessor(case)) and
|
||||
case.getValue().toInt() = k and
|
||||
inNonZeroCase = false
|
||||
)
|
||||
or
|
||||
// There's no implicit CompareInstruction in files compiled as C since C
|
||||
// doesn't have implicit boolean conversions. So instead we check whether
|
||||
// there's a branch on a value of pointer or integer type.
|
||||
relevantUnaryComparison(test) and
|
||||
op.getDef() = test and
|
||||
// Any instruction with an integral type could potentially be part of a
|
||||
// check for nullness when used in a guard. So we include all integral
|
||||
// typed instructions here. However, since some of these instructions are
|
||||
// already included as guards in other cases, we exclude those here.
|
||||
// These are instructions that compute a binary equality or inequality
|
||||
// relation. For example, the following:
|
||||
// ```cpp
|
||||
// if(a == b + 42) { ... }
|
||||
// ```
|
||||
// generates the following IR:
|
||||
// ```
|
||||
// r1(glval<int>) = VariableAddress[a] :
|
||||
// r2(int) = Load[a] : &:r1, m1
|
||||
// r3(glval<int>) = VariableAddress[b] :
|
||||
// r4(int) = Load[b] : &:r3, m2
|
||||
// r5(int) = Constant[42] :
|
||||
// r6(int) = Add : r4, r5
|
||||
// r7(bool) = CompareEQ : r2, r6
|
||||
// v1(void) = ConditionalBranch : r7
|
||||
// ```
|
||||
// and since `r7` is an integral typed instruction this predicate could
|
||||
// include a case for when `r7` evaluates to true (in which case we would
|
||||
// infer that `r6` was non-zero, and a case for when `r7` evaluates to false
|
||||
// (in which case we would infer that `r6` was zero).
|
||||
// However, since `a == b + 42` is already supported when reasoning about
|
||||
// binary equalities we exclude those cases here.
|
||||
not test.isGLValue() and
|
||||
not simple_comparison_eq(test, _, _, _, _) and
|
||||
not simple_comparison_lt(test, _, _, _) and
|
||||
not test = any(SwitchInstruction switch).getExpression() and
|
||||
(
|
||||
test.getResultIRType() instanceof IRAddressType or
|
||||
test.getResultIRType() instanceof IRIntegerType or
|
||||
test.getResultIRType() instanceof IRBooleanType
|
||||
) and
|
||||
(
|
||||
k = 1 and
|
||||
value.(BooleanValue).getValue() = true and
|
||||
@@ -871,12 +914,68 @@ private predicate unary_simple_comparison_eq(
|
||||
)
|
||||
}
|
||||
|
||||
/** A call to the builtin operation `__builtin_expect`. */
|
||||
private class BuiltinExpectCallInstruction extends CallInstruction {
|
||||
BuiltinExpectCallInstruction() { this.getStaticCallTarget().hasName("__builtin_expect") }
|
||||
|
||||
/** Gets the condition of this call. */
|
||||
Instruction getCondition() {
|
||||
// The first parameter of `__builtin_expect` has type `long`. So we skip
|
||||
// the conversion when inferring guards.
|
||||
result = this.getArgument(0).(ConvertInstruction).getUnary()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `left == right + k` is `areEqual` if `cmp` evaluates to `value`,
|
||||
* and `cmp` is an instruction that compares the value of
|
||||
* `__builtin_expect(left == right + k, _)` to `0`.
|
||||
*/
|
||||
private predicate builtin_expect_eq(
|
||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||
) {
|
||||
exists(BuiltinExpectCallInstruction call, Instruction const, AbstractValue innerValue |
|
||||
int_value(const) = 0 and
|
||||
cmp.hasOperands(call.getAUse(), const.getAUse()) and
|
||||
compares_eq(call.getCondition(), left, right, k, areEqual, innerValue)
|
||||
|
|
||||
cmp instanceof CompareNEInstruction and
|
||||
value = innerValue
|
||||
or
|
||||
cmp instanceof CompareEQInstruction and
|
||||
value.getDualValue() = innerValue
|
||||
)
|
||||
}
|
||||
|
||||
private predicate complex_eq(
|
||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||
) {
|
||||
sub_eq(cmp, left, right, k, areEqual, value)
|
||||
or
|
||||
add_eq(cmp, left, right, k, areEqual, value)
|
||||
or
|
||||
builtin_expect_eq(cmp, left, right, k, areEqual, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op == k` is `areEqual` if `cmp` evaluates to `value`, and `cmp` is
|
||||
* an instruction that compares the value of `__builtin_expect(op == k, _)` to `0`.
|
||||
*/
|
||||
private predicate unary_builtin_expect_eq(
|
||||
CompareInstruction cmp, Operand op, int k, boolean areEqual, boolean inNonZeroCase,
|
||||
AbstractValue value
|
||||
) {
|
||||
exists(BuiltinExpectCallInstruction call, Instruction const, AbstractValue innerValue |
|
||||
int_value(const) = 0 and
|
||||
cmp.hasOperands(call.getAUse(), const.getAUse()) and
|
||||
unary_compares_eq(call.getCondition(), op, k, areEqual, inNonZeroCase, innerValue)
|
||||
|
|
||||
cmp instanceof CompareNEInstruction and
|
||||
value = innerValue
|
||||
or
|
||||
cmp instanceof CompareEQInstruction and
|
||||
value.getDualValue() = innerValue
|
||||
)
|
||||
}
|
||||
|
||||
private predicate unary_complex_eq(
|
||||
@@ -885,6 +984,8 @@ private predicate unary_complex_eq(
|
||||
unary_sub_eq(test, op, k, areEqual, inNonZeroCase, value)
|
||||
or
|
||||
unary_add_eq(test, op, k, areEqual, inNonZeroCase, value)
|
||||
or
|
||||
unary_builtin_expect_eq(test, op, k, areEqual, inNonZeroCase, value)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -913,7 +1014,8 @@ private predicate compares_lt(
|
||||
|
||||
/** Holds if `op < k` evaluates to `isLt` given that `test` evaluates to `value`. */
|
||||
private predicate compares_lt(Instruction test, Operand op, int k, boolean isLt, AbstractValue value) {
|
||||
simple_comparison_lt(test, op, k, isLt, value)
|
||||
unary_simple_comparison_lt(test, k, isLt, value) and
|
||||
op.getDef() = test
|
||||
or
|
||||
complex_lt(test, op, k, isLt, value)
|
||||
or
|
||||
@@ -960,12 +1062,11 @@ private predicate simple_comparison_lt(CompareInstruction cmp, Operand left, Ope
|
||||
}
|
||||
|
||||
/** Rearrange various simple comparisons into `op < k` form. */
|
||||
private predicate simple_comparison_lt(
|
||||
Instruction test, Operand op, int k, boolean isLt, AbstractValue value
|
||||
private predicate unary_simple_comparison_lt(
|
||||
Instruction test, int k, boolean isLt, AbstractValue value
|
||||
) {
|
||||
exists(SwitchInstruction switch, CaseEdge case |
|
||||
test = switch.getExpression() and
|
||||
op.getDef() = test and
|
||||
case = value.(MatchValue).getCase() and
|
||||
exists(switch.getSuccessor(case)) and
|
||||
case.getMaxValue() > case.getMinValue()
|
||||
|
||||
@@ -14,16 +14,22 @@
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `namespace` column selects a namespace.
|
||||
* 2. The `type` column selects a type within that namespace.
|
||||
* 2. The `type` column selects a type within that namespace. This column can
|
||||
* introduce template names that can be mentioned in the `signature` column.
|
||||
* For example, `vector<T,Allocator>` introduces the template names `T` and
|
||||
* `Allocator`.
|
||||
* 3. The `subtypes` is a boolean that indicates whether to jump to an
|
||||
* arbitrary subtype of that type. Set this to `false` if leaving the `type`
|
||||
* blank (for example, a free function).
|
||||
* 4. The `name` column optionally selects a specific named member of the type.
|
||||
* Like the `type` column, this column can introduce template names that can
|
||||
* be mentioned in the `signature` column. For example, `insert<InputIt>`
|
||||
* introduces the template name `InputIt`.
|
||||
* 5. The `signature` column optionally restricts the named member. If
|
||||
* `signature` is blank then no such filtering is done. The format of the
|
||||
* signature is a comma-separated list of types enclosed in parentheses. The
|
||||
* types can be short names or fully qualified names (mixing these two options
|
||||
* is not allowed within a single signature).
|
||||
* types must be stripped of template names. That is, write `const vector &`
|
||||
* instead of `const vector<T> &`.
|
||||
* 6. The `ext` column specifies additional API-graph-like edges. Currently
|
||||
* there is only one valid value: "".
|
||||
* 7. The `input` column specifies how data enters the element selected by the
|
||||
@@ -44,6 +50,9 @@
|
||||
* One or more "*" can be added as an argument to indicate indirection, for
|
||||
* example, "ReturnValue[*]" indicates the first indirection of the return
|
||||
* value.
|
||||
* The special symbol `@` can be used to specify an arbitrary (but fixed)
|
||||
* number of indirections. For example, the `input` column `Argument[*@0]`
|
||||
* indicates one or more indirections of the 0th argument.
|
||||
*
|
||||
* An `output` can be either:
|
||||
* - "": Selects a read of a selected field.
|
||||
@@ -65,6 +74,17 @@
|
||||
* One or more "*" can be added as an argument to indicate indirection, for
|
||||
* example, "ReturnValue[*]" indicates the first indirection of the return
|
||||
* value.
|
||||
* The special symbol `@` can be used to specify an arbitrary (but fixed)
|
||||
* number of indirections. For example, the `output` column
|
||||
* `ReturnValue[*@0]` indicates one or more indirections of the return
|
||||
* value.
|
||||
* Note: The symbol `@` only ever takes a single value across a row. Thus,
|
||||
* the (`input`, `output`) pair `("Argument[*@0]", "ReturnValue[@]")`
|
||||
* represents:
|
||||
* - flow from the _first_ indirection of the 0th argument to the return
|
||||
* value, and
|
||||
* - flow from the _second_ indirection of the 0th argument to the first
|
||||
* indirection of the return value, etc.
|
||||
* 8. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources "remote" indicates a default remote flow source, and for summaries
|
||||
@@ -74,6 +94,8 @@
|
||||
|
||||
import cpp
|
||||
private import new.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as Private
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import internal.FlowSummaryImpl
|
||||
private import internal.FlowSummaryImpl::Public
|
||||
private import internal.FlowSummaryImpl::Private
|
||||
@@ -124,7 +146,7 @@ predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance
|
||||
string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
@@ -138,16 +160,20 @@ predicate sourceModel(
|
||||
row.splitAt(";", 6) = output and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
provenance = "manual" and
|
||||
model = ""
|
||||
or
|
||||
Extensions::sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance,
|
||||
_)
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
Extensions::sourceModel(namespace, type, subtypes, name, signature, ext, output, kind,
|
||||
provenance, madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate sinkModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance
|
||||
string input, string kind, string provenance, string model
|
||||
) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
@@ -161,15 +187,24 @@ predicate sinkModel(
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
provenance = "manual" and
|
||||
model = ""
|
||||
or
|
||||
Extensions::sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance, _)
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
Extensions::sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance,
|
||||
madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
/**
|
||||
* Holds if a summary model exists for the given parameters.
|
||||
*
|
||||
* This predicate does not expand `@` to `*`s.
|
||||
*/
|
||||
private predicate summaryModel0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance
|
||||
string input, string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(string row |
|
||||
summaryModel(row) and
|
||||
@@ -184,16 +219,48 @@ predicate summaryModel(
|
||||
row.splitAt(";", 7) = output and
|
||||
row.splitAt(";", 8) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
provenance = "manual" and
|
||||
model = ""
|
||||
or
|
||||
Extensions::summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, _)
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
Extensions::summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `input` is `input0`, but with all occurrences of `@` replaced
|
||||
* by `n` repetitions of `*` (and similarly for `output` and `output0`).
|
||||
*/
|
||||
bindingset[input0, output0, n]
|
||||
pragma[inline_late]
|
||||
private predicate expandInputAndOutput(
|
||||
string input0, string input, string output0, string output, int n
|
||||
) {
|
||||
input = input0.replaceAll("@", repeatStars(n)) and
|
||||
output = output0.replaceAll("@", repeatStars(n))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a summary model exists for the given parameters.
|
||||
*/
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(string input0, string output0 |
|
||||
summaryModel0(namespace, type, subtypes, name, signature, ext, input0, output0, kind,
|
||||
provenance, model) and
|
||||
expandInputAndOutput(input0, input, output0, output,
|
||||
[0 .. Private::getMaxElementContentIndirectionIndex() - 1])
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantNamespace(string namespace) {
|
||||
sourceModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
summaryModel(namespace, _, _, _, _, _, _, _, _, _)
|
||||
sourceModel(namespace, _, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(namespace, _, _, _, _, _, _, _, _, _) or
|
||||
summaryModel(namespace, _, _, _, _, _, _, _, _, _, _)
|
||||
}
|
||||
|
||||
private predicate namespaceLink(string shortns, string longns) {
|
||||
@@ -223,17 +290,17 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
|
||||
part = "source" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output, string provenance |
|
||||
string ext, string output, string provenance, string model |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance)
|
||||
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance, model)
|
||||
)
|
||||
or
|
||||
part = "sink" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string provenance |
|
||||
string ext, string input, string provenance, string model |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance)
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance, model)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
@@ -241,7 +308,7 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string output, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, provenance)
|
||||
summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, provenance, _)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -250,9 +317,9 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
|
||||
module CsvValidation {
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, AccessPath input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||
sinkModel(_, _, _, _, _, _, input, _, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _, _) and pred = "summary"
|
||||
summaryModel(_, _, _, _, _, _, input, _, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
invalidSpecComponent(input, part) and
|
||||
@@ -269,9 +336,9 @@ module CsvValidation {
|
||||
|
||||
private string getInvalidModelOutput() {
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||
sourceModel(_, _, _, _, _, _, output, _, _, _) and pred = "source"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _, _) and pred = "summary"
|
||||
summaryModel(_, _, _, _, _, _, _, output, _, _, _) and pred = "summary"
|
||||
|
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
@@ -281,11 +348,11 @@ module CsvValidation {
|
||||
}
|
||||
|
||||
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
|
||||
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) }
|
||||
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _, _) }
|
||||
|
||||
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) }
|
||||
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _, _) }
|
||||
|
||||
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) }
|
||||
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _, _) }
|
||||
}
|
||||
|
||||
private module KindVal = SharedModelVal::KindValidation<KindValConfig>;
|
||||
@@ -326,11 +393,11 @@ module CsvValidation {
|
||||
|
||||
private string getInvalidModelSignature() {
|
||||
exists(string pred, string namespace, string type, string name, string signature, string ext |
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "source"
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "source"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "sink"
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _, _) and pred = "summary"
|
||||
|
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\.:]*") and
|
||||
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
@@ -362,21 +429,160 @@ module CsvValidation {
|
||||
private predicate elementSpec(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _)
|
||||
}
|
||||
|
||||
/** Gets the fully templated version of `f`. */
|
||||
private Function getFullyTemplatedMemberFunction(Function f) {
|
||||
not f.isFromUninstantiatedTemplate(_) and
|
||||
exists(Class c, Class templateClass, int i |
|
||||
c.isConstructedFrom(templateClass) and
|
||||
f = c.getAMember(i) and
|
||||
result = templateClass.getCanonicalMember(i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type name of the `n`'th parameter of `f` without any template
|
||||
* arguments.
|
||||
*/
|
||||
bindingset[f]
|
||||
pragma[inline_late]
|
||||
string getParameterTypeWithoutTemplateArguments(Function f, int n) {
|
||||
exists(string s, string base, string specifiers |
|
||||
s = f.getParameter(n).getType().getName() and
|
||||
parseAngles(s, base, _, specifiers) and
|
||||
result = base + specifiers
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the `n`'th parameter of `f` by replacing template names
|
||||
* with `func:N` (where `N` is the index of the template).
|
||||
*/
|
||||
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
|
||||
exists(Function templateFunction |
|
||||
templateFunction = getFullyTemplatedMemberFunction(f) and
|
||||
remaining = templateFunction.getNumberOfTemplateArguments() and
|
||||
result = getParameterTypeWithoutTemplateArguments(templateFunction, n)
|
||||
)
|
||||
or
|
||||
exists(string mid, TemplateParameter tp, Function templateFunction |
|
||||
mid = getTypeNameWithoutFunctionTemplates(f, n, remaining + 1) and
|
||||
templateFunction = getFullyTemplatedMemberFunction(f) and
|
||||
tp = templateFunction.getTemplateArgument(remaining) and
|
||||
result = mid.replaceAll(tp.getName(), "func:" + remaining.toString())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the `n`'th parameter of `f` by replacing template names
|
||||
* with `class:N` (where `N` is the index of the template).
|
||||
*/
|
||||
private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining) {
|
||||
exists(Class template |
|
||||
f.getDeclaringType().isConstructedFrom(template) and
|
||||
remaining = template.getNumberOfTemplateArguments() and
|
||||
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
|
||||
)
|
||||
or
|
||||
exists(string mid, TemplateParameter tp, Class template |
|
||||
mid = getTypeNameWithoutClassTemplates(f, n, remaining + 1) and
|
||||
f.getDeclaringType().isConstructedFrom(template) and
|
||||
tp = template.getTemplateArgument(remaining) and
|
||||
result = mid.replaceAll(tp.getName(), "class:" + remaining.toString())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the string representation of the `i`'th parameter of `c`. */
|
||||
private string getParameterTypeName(Function c, int i) {
|
||||
result = getTypeNameWithoutClassTemplates(c, i, 0)
|
||||
}
|
||||
|
||||
/** Splits `s` by `,` and gets the `i`'th element. */
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private string getAtIndex(string s, int i) {
|
||||
result = s.splitAt(",", i) and
|
||||
// when `s` is `""` and `i` is `0` we get `result = ""` which we don't want.
|
||||
not (s = "" and i = 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes `partiallyNormalizedSignature` by replacing the `remaining`
|
||||
* number of template arguments in `partiallyNormalizedSignature` with their
|
||||
* index in `typeArgs`.
|
||||
*/
|
||||
private string getSignatureWithoutClassTemplateNames(
|
||||
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
|
||||
) {
|
||||
elementSpecWithArguments0(_, _, _, partiallyNormalizedSignature, typeArgs, nameArgs) and
|
||||
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
|
||||
result = partiallyNormalizedSignature
|
||||
or
|
||||
exists(string mid |
|
||||
mid =
|
||||
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs,
|
||||
remaining + 1)
|
||||
|
|
||||
exists(string typeArg |
|
||||
typeArg = getAtIndex(typeArgs, remaining) and
|
||||
result = mid.replaceAll(typeArg, "class:" + remaining.toString())
|
||||
)
|
||||
or
|
||||
// Make sure `remaining` is properly bound
|
||||
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
|
||||
not exists(getAtIndex(typeArgs, remaining)) and
|
||||
result = mid
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes `partiallyNormalizedSignature` by replacing:
|
||||
* - _All_ the template arguments in `partiallyNormalizedSignature` that refer to
|
||||
* template parameters in `typeArgs` with their index in `typeArgs`, and
|
||||
* - The `remaining` number of template arguments in `partiallyNormalizedSignature`
|
||||
* with their index in `nameArgs`.
|
||||
*/
|
||||
private string getSignatureWithoutFunctionTemplateNames(
|
||||
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
|
||||
) {
|
||||
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
|
||||
result =
|
||||
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs, 0)
|
||||
or
|
||||
exists(string mid |
|
||||
mid =
|
||||
getSignatureWithoutFunctionTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs,
|
||||
remaining + 1)
|
||||
|
|
||||
exists(string nameArg |
|
||||
nameArg = getAtIndex(nameArgs, remaining) and
|
||||
result = mid.replaceAll(nameArg, "func:" + remaining.toString())
|
||||
)
|
||||
or
|
||||
// Make sure `remaining` is properly bound
|
||||
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
|
||||
not exists(getAtIndex(nameArgs, remaining)) and
|
||||
result = mid
|
||||
)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function c, int i) {
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
exists(int n, string p | c.getParameter(n).getType().toString() = p |
|
||||
i = 2 * n and result = p
|
||||
not c.isFromUninstantiatedTemplate(_) and
|
||||
(
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
exists(int n, string p | getParameterTypeName(c, n) = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,6 +602,193 @@ private predicate matchesSignature(Function func, string signature) {
|
||||
paramsString(func) = signature
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(_, type, _, name, signature, _)` holds and
|
||||
* - `typeArgs` represents the named template parameters supplied to `type`, and
|
||||
* - `nameArgs` represents the named template parameters supplied to `name`, and
|
||||
* - `normalizedSignature` is `signature`, except with
|
||||
* - template parameter names replaced by `func:i` if the template name is
|
||||
* the `i`'th entry in `nameArgs`, and
|
||||
* - template parameter names replaced by `class:i` if the template name is
|
||||
* the `i`'th entry in `typeArgs`.
|
||||
*
|
||||
* In other words, the string `normalizedSignature` represents a "normalized"
|
||||
* signature with no mention of any free template parameters.
|
||||
*
|
||||
* For example, consider a summary row such as:
|
||||
* ```
|
||||
* elementSpec(_, "MyClass<B, C>", _, myFunc<A>, "(const A &,int,C,B *)", _)
|
||||
* ```
|
||||
* In this case, `normalizedSignature` will be `"(const func:0 &,int,class:1,class:0 *)"`.
|
||||
*/
|
||||
private predicate elementSpecWithArguments(
|
||||
string signature, string type, string name, string normalizedSignature, string typeArgs,
|
||||
string nameArgs
|
||||
) {
|
||||
exists(string signatureWithoutParens |
|
||||
elementSpecWithArguments0(signature, type, name, signatureWithoutParens, typeArgs, nameArgs) and
|
||||
normalizedSignature =
|
||||
getSignatureWithoutFunctionTemplateNames(signatureWithoutParens, typeArgs, nameArgs, 0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the `n`'th normalized signature parameter for the function `name` in class `type`. */
|
||||
private string getSignatureParameterName(string signature, string type, string name, int n) {
|
||||
exists(string normalizedSignature |
|
||||
elementSpecWithArguments(signature, type, name, normalizedSignature, _, _) and
|
||||
result = getAtIndex(normalizedSignature, n)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the suffix containing the entries in `signature` starting at entry
|
||||
* `i` matches the suffix containing the parameters of `func` starting at entry `i`.
|
||||
*
|
||||
* For example, consider the signature `(int,bool,char)` and a function:
|
||||
* ```
|
||||
* void f(int a, bool b, char c);
|
||||
* ```
|
||||
* 1. The predicate holds for `i = 2` because the suffix containing all the entries
|
||||
* in `signature` starting at `2` is `char`, and suffix containing all the parameters
|
||||
* of `func` starting at `2` is `char`.
|
||||
* 2. The predicate holds for `i = 1` because the suffix containing all the entries
|
||||
* in `signature` starting at `1` is `bool,char`, and the suffix containing all the
|
||||
* parameters of `func` starting at `1` is `bool, char`.
|
||||
* 3. The predicate holds for `i = 0` because the suffix containing all the entries
|
||||
* in `signature` starting at `0` is `int,bool,char` and the suffix containing all
|
||||
* the parameters of `func` starting at `0` is `int, bool, char`.
|
||||
*
|
||||
* When `paramsString(func)[i]` is `class:n` then the signature name is
|
||||
* compared with the `n`'th name in `type`, and when `paramsString(func)[i]`
|
||||
* is `func:n` then the signature name is compared with the `n`'th name
|
||||
* in `name`.
|
||||
*/
|
||||
private predicate signatureMatches(Function func, string signature, string type, string name, int i) {
|
||||
exists(string s |
|
||||
s = getSignatureParameterName(signature, type, name, i) and
|
||||
s = getParameterTypeName(func, i)
|
||||
) and
|
||||
if exists(getParameterTypeName(func, i + 1))
|
||||
then signatureMatches(func, signature, type, name, i + 1)
|
||||
else i = count(signature.indexOf(","))
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal: Do not use.
|
||||
*
|
||||
* This module only exists to expose internal predicates for testing purposes.
|
||||
*/
|
||||
module ExternalFlowDebug {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Exposed for testing purposes.
|
||||
*/
|
||||
predicate signatureMatches_debug = signatureMatches/5;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Exposed for testing purposes.
|
||||
*/
|
||||
predicate getSignatureParameterName_debug = getSignatureParameterName/4;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Exposed for testing purposes.
|
||||
*/
|
||||
predicate getParameterTypeName_debug = getParameterTypeName/2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `s` can be broken into a string of the form
|
||||
* `beforeAngles<betweenAngles>`,
|
||||
* or `s = beforeAngles` where `beforeAngles` does not have any brackets.
|
||||
*/
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private predicate parseAngles(
|
||||
string s, string beforeAngles, string betweenAngles, string afterAngles
|
||||
) {
|
||||
beforeAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 1) and
|
||||
(
|
||||
betweenAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 2) and
|
||||
afterAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 3)
|
||||
or
|
||||
not exists(s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 2)) and
|
||||
betweenAngles = "" and
|
||||
afterAngles = ""
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `s` can be broken into a string of the form `(betweenParens)`. */
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private predicate parseParens(string s, string betweenParens) { s = "(" + betweenParens + ")" }
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(_, type, _, name, signature, _)` and:
|
||||
* - `type` introduces template parameters `typeArgs`, and
|
||||
* - `name` introduces template parameters `nameArgs`, and
|
||||
* - `signatureWithoutParens` equals `signature`, but with the surrounding
|
||||
* parentheses removed.
|
||||
*/
|
||||
private predicate elementSpecWithArguments0(
|
||||
string signature, string type, string name, string signatureWithoutParens, string typeArgs,
|
||||
string nameArgs
|
||||
) {
|
||||
elementSpec(_, type, _, name, signature, _) and
|
||||
parseAngles(name, _, nameArgs, "") and
|
||||
(
|
||||
type = "" and typeArgs = ""
|
||||
or
|
||||
parseAngles(type, _, typeArgs, "")
|
||||
) and
|
||||
parseParens(signature, signatureWithoutParens)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(namespace, type, subtypes, name, signature, _)` and
|
||||
* `method`'s signature matches `signature`.
|
||||
*
|
||||
* `signature` may contain template parameter names that are bound by `type` and `name`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate elementSpecMatchesSignature(
|
||||
Function method, string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, pragma[only_bind_into](type), subtypes, pragma[only_bind_into](name),
|
||||
pragma[only_bind_into](signature), _) and
|
||||
signatureMatches(method, signature, type, name, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `classWithMethod` has `method` named `name` (excluding any
|
||||
* template parameters).
|
||||
*/
|
||||
bindingset[name]
|
||||
pragma[inline_late]
|
||||
private predicate hasClassAndName(Class classWithMethod, Function method, string name) {
|
||||
exists(string nameWithoutArgs |
|
||||
parseAngles(name, nameWithoutArgs, _, "") and
|
||||
classWithMethod = method.getClassAndName(nameWithoutArgs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `namedClass` is in namespace `namespace` and has
|
||||
* name `type` (excluding any template parameters).
|
||||
*/
|
||||
bindingset[type, namespace]
|
||||
pragma[inline_late]
|
||||
private predicate hasQualifiedName(Class namedClass, string namespace, string type) {
|
||||
exists(string typeWithoutArgs |
|
||||
parseAngles(type, typeWithoutArgs, _, "") and
|
||||
namedClass.hasQualifiedName(namespace, typeWithoutArgs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the element in module `namespace` that satisfies the following properties:
|
||||
* 1. If the element is a member of a class-like type, then the class-like type has name `type`
|
||||
@@ -410,8 +803,8 @@ pragma[nomagic]
|
||||
private Element interpretElement0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
(
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
// Non-member functions
|
||||
exists(Function func |
|
||||
func.hasQualifiedName(namespace, name) and
|
||||
@@ -423,21 +816,28 @@ private Element interpretElement0(
|
||||
)
|
||||
or
|
||||
// Member functions
|
||||
exists(Class namedClass, Class classWithMethod, Function method |
|
||||
classWithMethod = method.getClassAndName(name) and
|
||||
namedClass.hasQualifiedName(namespace, type) and
|
||||
matchesSignature(method, signature) and
|
||||
result = method
|
||||
|
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
classWithMethod = namedClass.getADerivedClass*()
|
||||
or
|
||||
// member declared directly in the named type
|
||||
subtypes = false and
|
||||
classWithMethod = namedClass
|
||||
exists(Class namedClass, Class classWithMethod |
|
||||
(
|
||||
elementSpecMatchesSignature(result, namespace, type, subtypes, name, signature) and
|
||||
hasClassAndName(classWithMethod, result, name)
|
||||
or
|
||||
signature = "" and
|
||||
elementSpec(namespace, type, subtypes, name, "", _) and
|
||||
hasClassAndName(classWithMethod, result, name)
|
||||
) and
|
||||
hasQualifiedName(namedClass, namespace, type) and
|
||||
(
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
classWithMethod = namedClass.getADerivedClass*()
|
||||
or
|
||||
// member declared directly in the named type
|
||||
subtypes = false and
|
||||
classWithMethod = namedClass
|
||||
)
|
||||
)
|
||||
or
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
// Member variables
|
||||
signature = "" and
|
||||
exists(Class namedClass, Class classWithMember, MemberVariable member |
|
||||
@@ -456,6 +856,7 @@ private Element interpretElement0(
|
||||
)
|
||||
or
|
||||
// Global or namespace variables
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
signature = "" and
|
||||
type = "" and
|
||||
subtypes = false and
|
||||
@@ -480,9 +881,9 @@ private module Cached {
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(DataFlow::Node node, string kind) {
|
||||
predicate sourceNode(DataFlow::Node node, string kind, string model) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSourceNode(n, kind, _) and n.asNode() = node // TODO
|
||||
isSourceNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
|
||||
@@ -491,40 +892,57 @@ private module Cached {
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(DataFlow::Node node, string kind) {
|
||||
predicate sinkNode(DataFlow::Node node, string kind, string model) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSinkNode(n, kind, _) and n.asNode() = node // TODO
|
||||
isSinkNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
predicate sourceNode(DataFlow::Node node, string kind) { sourceNode(node, kind, _) }
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
predicate sinkNode(DataFlow::Node node, string kind) { sinkNode(node, kind, _) }
|
||||
|
||||
private predicate interpretSummary(
|
||||
Function f, string input, string output, string kind, string provenance
|
||||
Function f, string input, string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance,
|
||||
model) and
|
||||
f = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||
private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _) }
|
||||
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _, _) }
|
||||
|
||||
private predicate relevantSummaryElementManual(string input, string output, string kind) {
|
||||
private predicate relevantSummaryElementManual(
|
||||
string input, string output, string kind, string model
|
||||
) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
interpretSummary(this, input, output, kind, provenance, model) and
|
||||
provenance.isManual()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
|
||||
private predicate relevantSummaryElementGenerated(
|
||||
string input, string output, string kind, string model
|
||||
) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
interpretSummary(this, input, output, kind, provenance, model) and
|
||||
provenance.isGenerated()
|
||||
)
|
||||
}
|
||||
@@ -533,18 +951,17 @@ private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
string input, string output, boolean preservesValue, string model
|
||||
) {
|
||||
exists(string kind |
|
||||
this.relevantSummaryElementManual(input, output, kind)
|
||||
this.relevantSummaryElementManual(input, output, kind, model)
|
||||
or
|
||||
not this.relevantSummaryElementManual(_, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind)
|
||||
not this.relevantSummaryElementManual(_, _, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind, model)
|
||||
|
|
||||
if kind = "value" then preservesValue = true else preservesValue = false
|
||||
) and
|
||||
model = "" // TODO
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) {
|
||||
interpretSummary(this, _, _, _, provenance)
|
||||
interpretSummary(this, _, _, _, provenance, _)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,25 +215,19 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
|
||||
predicate localMustFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
Type getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
DataFlowType getNodeType(Node n) {
|
||||
exists(n) and
|
||||
result instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getNodeType`. */
|
||||
string ppReprType(Type t) { none() } // stub implementation
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(Type t1, Type t2) {
|
||||
any() // stub implementation
|
||||
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
|
||||
t1 instanceof VoidType and t2 instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
private predicate suppressUnusedNode(Node n) { any() }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -242,21 +236,15 @@ class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
class DataFlowCallable extends Function {
|
||||
/** Gets a best-effort total ordering. */
|
||||
int totalorder() {
|
||||
this =
|
||||
rank[result](DataFlowCallable c, string file, int startline, int startcolumn |
|
||||
c.getLocation().hasLocationInfo(file, startline, startcolumn, _, _)
|
||||
|
|
||||
c order by file, startline, startcolumn
|
||||
)
|
||||
}
|
||||
}
|
||||
class DataFlowCallable extends Function { }
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
class DataFlowType = Type;
|
||||
final private class TypeFinal = Type;
|
||||
|
||||
class DataFlowType extends TypeFinal {
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends Expr instanceof Call {
|
||||
@@ -272,24 +260,12 @@ class DataFlowCall extends Expr instanceof Call {
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
DataFlowCallable getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
|
||||
/** Gets a best-effort total ordering. */
|
||||
int totalorder() {
|
||||
this =
|
||||
rank[result](DataFlowCall c, int startline, int startcolumn |
|
||||
c.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
||||
|
|
||||
c order by startline, startcolumn
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class NodeRegion instanceof Unit {
|
||||
string toString() { result = "NodeRegion" }
|
||||
|
||||
predicate contains(Node n) { none() }
|
||||
|
||||
int totalOrder() { result = 1 }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
@@ -35,16 +35,22 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
result = "Field" and
|
||||
arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName()
|
||||
)
|
||||
or
|
||||
exists(ElementContent ec |
|
||||
cs.isSingleton(ec) and
|
||||
result = "Element" and
|
||||
arg = repeatStars(ec.getIndirectionIndex() - 1)
|
||||
)
|
||||
}
|
||||
|
||||
string encodeWithoutContent(ContentSet c, string arg) {
|
||||
// used for type tracking, not currently used in C/C++.
|
||||
result = "WithoutContent" + c and arg = ""
|
||||
none()
|
||||
}
|
||||
|
||||
string encodeWithContent(ContentSet c, string arg) {
|
||||
// used for type tracking, not currently used in C/C++.
|
||||
result = "WithContent" + c and arg = ""
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,25 +85,6 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
token.getName() = "Parameter" and
|
||||
result = decodePosition(token.getAnArgument())
|
||||
}
|
||||
|
||||
bindingset[token]
|
||||
ContentSet decodeUnknownContent(AccessPath::AccessPathTokenBase token) {
|
||||
// field content (no indirection support)
|
||||
exists(FieldContent c |
|
||||
result.isSingleton(c) and
|
||||
token.getName() = c.getField().getName() and
|
||||
not exists(token.getArgumentList()) and
|
||||
c.getIndirectionIndex() = 1
|
||||
)
|
||||
or
|
||||
// field content (with indirection support)
|
||||
exists(FieldContent c |
|
||||
result.isSingleton(c) and
|
||||
token.getName() = c.getField().getName() and
|
||||
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
|
||||
token.getAnArgument() = repeatStars(c.getIndirectionIndex() - 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Make<Location, DataFlowImplSpecific::CppDataFlow, Input> as Impl
|
||||
@@ -125,9 +112,8 @@ module SourceSinkInterpretationInput implements
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext) and
|
||||
model = "" // TODO
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance, model) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -141,9 +127,8 @@ module SourceSinkInterpretationInput implements
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
|
||||
e = interpretElement(package, type, subtypes, name, signature, ext) and
|
||||
model = "" // TODO
|
||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance, model) and
|
||||
e = interpretElement(package, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -383,6 +383,37 @@ class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr {
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsConvertibleTo" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_convertible` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the first type can be converted to the second type.
|
||||
* ```
|
||||
* bool v = __is_convertible(MyType, OtherType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsConvertible extends BuiltInOperation, @isconvertible {
|
||||
override string toString() { result = "__is_convertible" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsConvertible" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_nothrow_convertible` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the first type can be converted to the second type and the
|
||||
* conversion operator has an empty exception specification.
|
||||
* ```
|
||||
* bool v = __is_nothrow_convertible(MyType, OtherType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsNothrowConvertible extends BuiltInOperation, @isnothrowconvertible {
|
||||
override string toString() { result = "__is_nothrow_convertible" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowConvertible" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_empty` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
@@ -647,8 +678,7 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial
|
||||
* The `__is_nothrow_assignable` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns true if there exists a `C::operator =(const D& d) nothrow`
|
||||
* assignment operator (i.e, with an empty exception specification).
|
||||
* Returns true if there exists an assignment operator with an empty exception specification.
|
||||
* ```
|
||||
* bool v = __is_nothrow_assignable(MyType1, MyType2);
|
||||
* ```
|
||||
@@ -663,8 +693,7 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas
|
||||
* The `__is_assignable` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns true if there exists a `C::operator =(const D& d)` assignment
|
||||
* operator.
|
||||
* Returns true if there exists an assignment operator.
|
||||
* ```
|
||||
* bool v = __is_assignable(MyType1, MyType2);
|
||||
* ```
|
||||
@@ -675,6 +704,25 @@ class BuiltInOperationIsAssignable extends BuiltInOperation, @isassignable {
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsAssignable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_assignable_no_precondition_check` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns true if there exists an assignment operator.
|
||||
* ```
|
||||
* bool v = __is_assignable_no_precondition_check(MyType1, MyType2);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsAssignableNoPreconditionCheck extends BuiltInOperation,
|
||||
@isassignablenopreconditioncheck
|
||||
{
|
||||
override string toString() { result = "__is_assignable_no_precondition_check" }
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
result = "BuiltInOperationIsAssignableNoPreconditionCheck"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_standard_layout` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
@@ -708,6 +756,20 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyCopyable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_trivially_copy_assignable` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if instances of this type can be copied using a trivial
|
||||
* copy operator.
|
||||
*/
|
||||
class BuiltInOperationIsTriviallyCopyAssignable extends BuiltInOperation, @istriviallycopyassignable
|
||||
{
|
||||
override string toString() { result = "__is_trivially_copy_assignable" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyCopyAssignable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_literal_type` built-in operation (used by some implementations of
|
||||
* the `<type_traits>` header).
|
||||
@@ -1062,6 +1124,24 @@ class BuiltInOperationIsSame extends BuiltInOperation, @issame {
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsSame" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_same_as` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if two types are the same.
|
||||
* ```
|
||||
* template<typename _Tp, typename _Up>
|
||||
* struct is_same
|
||||
* : public integral_constant<bool, __is_same_as(_Tp, _Up)>
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsSameAs extends BuiltInOperation, @issameas {
|
||||
override string toString() { result = "__is_same_as" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsSameAs" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_function` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
@@ -1120,6 +1200,87 @@ class BuiltInOperationIsPointerInterconvertibleBaseOf extends BuiltInOperation,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_pointer_interconvertible_with_class` built-in operation (used
|
||||
* by some implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a member pointer is pointer-interconvertible with a
|
||||
* class type.
|
||||
* ```
|
||||
* template<typename _Tp, typename _Up>
|
||||
* constexpr bool is_pointer_interconvertible_with_class(_Up _Tp::*mp) noexcept
|
||||
* = __is_pointer_interconvertible_with_class(_Tp, mp);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsPointerInterconvertibleWithClass extends BuiltInOperation,
|
||||
@ispointerinterconvertiblewithclass
|
||||
{
|
||||
override string toString() { result = "__is_pointer_interconvertible_with_class" }
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
result = "BuiltInOperationIsPointerInterconvertibleWithClass"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__builtin_is_pointer_interconvertible_with_class` built-in operation (used
|
||||
* by some implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a member pointer is pointer-interconvertible with a class type.
|
||||
* ```
|
||||
* template<typename _Tp, typename _Up>
|
||||
* constexpr bool is_pointer_interconvertible_with_class(_Up _Tp::*mp) noexcept
|
||||
* = __builtin_is_pointer_interconvertible_with_class(mp);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationBuiltInIsPointerInterconvertible extends BuiltInOperation,
|
||||
@builtinispointerinterconvertiblewithclass
|
||||
{
|
||||
override string toString() { result = "__builtin_is_pointer_interconvertible_with_class" }
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
result = "BuiltInOperationBuiltInIsPointerInterconvertible"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_corresponding_member` built-in operation (used
|
||||
* by some implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if two member pointers refer to corresponding
|
||||
* members in the initial sequences of two class types.
|
||||
* ```
|
||||
* template<typename _Tp1, typename _Tp2, typename _Up1, typename _Up2>
|
||||
* constexpr bool is_corresponding_member(_Up1 _Tp1::*mp1, _Up2 _Tp2::*mp2 ) noexcept
|
||||
* = __is_corresponding_member(_Tp1, _Tp2, mp1, mp2);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsCorrespondingMember extends BuiltInOperation, @iscorrespondingmember {
|
||||
override string toString() { result = "__is_corresponding_member" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsCorrespondingMember" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__builtin_is_corresponding_member` built-in operation (used
|
||||
* by some implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if two member pointers refer to corresponding
|
||||
* members in the initial sequences of two class types.
|
||||
* ```
|
||||
* template<typename _Tp1, typename _Tp2, typename _Up1, typename _Up2>
|
||||
* constexpr bool is_corresponding_member(_Up1 _Tp1::*mp1, _Up2 _Tp2::*mp2 ) noexcept
|
||||
* = __builtin_is_corresponding_member(mp1, mp2);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationBuiltInIsCorrespondingMember extends BuiltInOperation,
|
||||
@builtiniscorrespondingmember
|
||||
{
|
||||
override string toString() { result = "__builtin_is_corresponding_member" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInIsCorrespondingMember" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_array` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
@@ -1138,6 +1299,42 @@ class BuiltInOperationIsArray extends BuiltInOperation, @isarray {
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsArray" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_bounded_array` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a type is a bounded array type.
|
||||
* ```
|
||||
* template<typename _Tp>
|
||||
* struct is_bounded_array
|
||||
* : public integral_constant<bool, __is_bounded_array(_Tp)>
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsBoundedArray extends BuiltInOperation, @isboundedarray {
|
||||
override string toString() { result = "__is_bounded_array" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsBoundedArray" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_unbounded_array` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a type is an unbounded array type.
|
||||
* ```
|
||||
* template<typename _Tp>
|
||||
* struct is_bounded_array
|
||||
* : public integral_constant<bool, __is_unbounded_array(_Tp)>
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsUnboundedArray extends BuiltInOperation, @isunboundedarray {
|
||||
override string toString() { result = "__is_unbounded_array" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsUnboundedArray" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__array_rank` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
@@ -1554,10 +1751,10 @@ class BuiltInBitCast extends BuiltInOperation, @builtinbitcast {
|
||||
*
|
||||
* Returns `true` if a type is a trivial type.
|
||||
* ```
|
||||
* template<typename _Tp>
|
||||
* struct is_trivial
|
||||
* : public integral_constant<bool, __is_trivial(_Tp)>
|
||||
* {};
|
||||
* template<typename _Tp>
|
||||
* struct is_trivial
|
||||
* : public integral_constant<bool, __is_trivial(_Tp)>
|
||||
* {};
|
||||
* ```
|
||||
*/
|
||||
class BuiltInIsTrivial extends BuiltInOperation, @istrivialexpr {
|
||||
@@ -1565,3 +1762,126 @@ class BuiltInIsTrivial extends BuiltInOperation, @istrivialexpr {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInIsTrivial" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__reference_constructs_from_temporary` built-in operation
|
||||
* (used by some implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a reference type `_Tp` is bound to an expression of
|
||||
* type `_Up` in direct-initialization, and a temporary object is bound.
|
||||
* ```
|
||||
* template<typename _Tp, typename _Up>
|
||||
* struct reference_constructs_from_temporary
|
||||
* : public integral_constant<bool, __reference_constructs_from_temporary(_Tp, _Up)>
|
||||
* {};
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationReferenceConstructsFromTemporary extends BuiltInOperation,
|
||||
@referenceconstructsfromtemporary
|
||||
{
|
||||
override string toString() { result = "__reference_constructs_from_temporary" }
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
result = "BuiltInOperationReferenceConstructsFromTemporary"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__reference_converts_from_temporary` built-in operation
|
||||
* (used by some implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a reference type `_Tp` is bound to an expression of
|
||||
* type `_Up` in copy-initialization, and a temporary object is bound.
|
||||
* ```
|
||||
* template<typename _Tp, typename _Up>
|
||||
* struct reference_converts_from_temporary
|
||||
* : public integral_constant<bool, __reference_converts_from_temporary(_Tp, _Up)>
|
||||
* {};
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationReferenceCovertsFromTemporary extends BuiltInOperation,
|
||||
@referenceconvertsfromtemporary
|
||||
{
|
||||
override string toString() { result = "__reference_converts_from_temporary" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationReferenceCovertsFromTemporary" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__reference_binds_to_temporary` built-in operation (used by some
|
||||
* implementations of the `<tuple>` header).
|
||||
*
|
||||
* Returns `true` if a reference of type `Type1` is bound to an expression of
|
||||
* type `Type1`, and a temporary object is bound.
|
||||
* ```
|
||||
* __reference_binds_to_temporary(Type1, Type2)
|
||||
*/
|
||||
class BuiltInOperationReferenceBindsToTemporary extends BuiltInOperation, @referencebindstotemporary
|
||||
{
|
||||
override string toString() { result = "__reference_binds_to_temporary" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationReferenceBindsToTemporary" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__builtin_has_attribute` built-in operation.
|
||||
*
|
||||
* Returns `true` if a type or expression has been declared with the
|
||||
* specified attribute.
|
||||
* ```
|
||||
* __attribute__ ((aligned(8))) int v;
|
||||
* bool has_attribute = __builtin_has_attribute(v, aligned);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasAttribute extends BuiltInOperation, @builtinhasattribute {
|
||||
override string toString() { result = "__builtin_has_attribute" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationHasAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_referenceable` built-in operation.
|
||||
*
|
||||
* Returns `true` if a type can be referenced.
|
||||
* ```
|
||||
* bool is_referenceable = __is_referenceable(int);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsReferenceable extends BuiltInOperation, @isreferenceable {
|
||||
override string toString() { result = "__is_referenceable" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsReferenceable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_valid_winrt_type` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if the type is a valid WinRT type.
|
||||
*/
|
||||
class BuiltInOperationIsValidWinRtType extends BuiltInOperation, @isvalidwinrttype {
|
||||
override string toString() { result = "__is_valid_winrt_type" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsValidWinRtType" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_win_class` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if the class is a ref class.
|
||||
*/
|
||||
class BuiltInOperationIsWinClass extends BuiltInOperation, @iswinclass {
|
||||
override string toString() { result = "__is_win_class" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsWinClass" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_win_class` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if the class is an interface class.
|
||||
*/
|
||||
class BuiltInOperationIsWinInterface extends BuiltInOperation, @iswininterface {
|
||||
override string toString() { result = "__is_win_interface" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsWinInterface" }
|
||||
}
|
||||
|
||||
@@ -855,6 +855,16 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the deallocation function is a destroying delete.
|
||||
*/
|
||||
predicate isDestroyingDeleteDeallocation() {
|
||||
exists(int form |
|
||||
expr_deallocator(underlyingElement(this), _, form) and
|
||||
form.bitAnd(4) != 0 // Bit two is the "destroying delete" bit
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type that is being allocated.
|
||||
*
|
||||
@@ -949,6 +959,16 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
|
||||
* gives nothing, as the 10 is considered part of the type.
|
||||
*/
|
||||
Expr getExtent() { result = this.getChild(2) }
|
||||
|
||||
/**
|
||||
* Gets the number of elements in the array, if available.
|
||||
*
|
||||
* For example, `new int[]{1,2,3}` has an array size of 3.
|
||||
*/
|
||||
int getArraySize() {
|
||||
result = this.getAllocatedType().(ArrayType).getArraySize() or
|
||||
result = this.getInitializer().(ArrayAggregateLiteral).getArraySize()
|
||||
}
|
||||
}
|
||||
|
||||
private class TDeleteOrDeleteArrayExpr = @delete_expr or @delete_array_expr;
|
||||
@@ -1015,6 +1035,16 @@ class DeleteOrDeleteArrayExpr extends Expr, TDeleteOrDeleteArrayExpr {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the deallocation function is a destroying delete.
|
||||
*/
|
||||
predicate isDestroyingDeleteDeallocation() {
|
||||
exists(int form |
|
||||
expr_deallocator(underlyingElement(this), _, form) and
|
||||
form.bitAnd(4) != 0 // Bit two is the "destroying delete" bit
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object or array being deleted.
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ private import Node0ToString
|
||||
private import ModelUtil
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as IO
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DF
|
||||
private import semmle.code.cpp.dataflow.ExternalFlow as External
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
@@ -412,6 +413,8 @@ class ArgumentPosition = Position;
|
||||
|
||||
abstract class Position extends TPosition {
|
||||
abstract string toString();
|
||||
|
||||
abstract int getIndirectionIndex();
|
||||
}
|
||||
|
||||
class DirectPosition extends Position, TDirectPosition {
|
||||
@@ -421,13 +424,15 @@ class DirectPosition extends Position, TDirectPosition {
|
||||
|
||||
override string toString() {
|
||||
index = -1 and
|
||||
result = "this"
|
||||
result = "this pointer"
|
||||
or
|
||||
index != -1 and
|
||||
result = index.toString()
|
||||
}
|
||||
|
||||
int getIndex() { result = index }
|
||||
|
||||
final override int getIndirectionIndex() { result = 0 }
|
||||
}
|
||||
|
||||
class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
@@ -438,16 +443,13 @@ class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
|
||||
override string toString() {
|
||||
if argumentIndex = -1
|
||||
then if indirectionIndex > 0 then result = "this indirection" else result = "this"
|
||||
else
|
||||
if indirectionIndex > 0
|
||||
then result = argumentIndex.toString() + " indirection"
|
||||
else result = argumentIndex.toString()
|
||||
then result = repeatStars(indirectionIndex - 1) + "this"
|
||||
else result = repeatStars(indirectionIndex) + argumentIndex.toString()
|
||||
}
|
||||
|
||||
int getArgumentIndex() { result = argumentIndex }
|
||||
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
final override int getIndirectionIndex() { result = indirectionIndex }
|
||||
}
|
||||
|
||||
newtype TPosition =
|
||||
@@ -988,24 +990,18 @@ predicate localMustFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
DataFlowType getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
exists(n) and
|
||||
result instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getNodeType`. */
|
||||
string ppReprType(DataFlowType t) { none() } // stub implementation
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
|
||||
any() // stub implementation
|
||||
t1 instanceof VoidType and t2 instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
private predicate suppressUnusedNode(Node n) { any() }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1062,16 +1058,6 @@ class DataFlowCallable extends TDataFlowCallable {
|
||||
result = this.asSummarizedCallable() or // SummarizedCallable = Function (in CPP)
|
||||
result = this.asSourceCallable()
|
||||
}
|
||||
|
||||
/** Gets a best-effort total ordering. */
|
||||
int totalorder() {
|
||||
this =
|
||||
rank[result](DataFlowCallable c, string file, int startline, int startcolumn |
|
||||
c.getLocation().hasLocationInfo(file, startline, startcolumn, _, _)
|
||||
|
|
||||
c order by file, startline, startcolumn
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1108,7 +1094,11 @@ class SummarizedCallable extends DataFlowCallable, TSummarizedCallable {
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
class DataFlowType = Type;
|
||||
final private class TypeFinal = Type;
|
||||
|
||||
class DataFlowType extends TypeFinal {
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDataFlowCall =
|
||||
@@ -1169,16 +1159,6 @@ class DataFlowCall extends TDataFlowCall {
|
||||
* Gets the location of this call.
|
||||
*/
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets a best-effort total ordering. */
|
||||
int totalorder() {
|
||||
this =
|
||||
rank[result](DataFlowCall c, int startline, int startcolumn |
|
||||
c.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
||||
|
|
||||
c order by startline, startcolumn
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1271,15 +1251,6 @@ module IsUnreachableInCall {
|
||||
string toString() { result = "NodeRegion" }
|
||||
|
||||
predicate contains(Node n) { this = n.getBasicBlock() }
|
||||
|
||||
int totalOrder() {
|
||||
this =
|
||||
rank[result](IRBlock b, int startline, int startcolumn |
|
||||
b.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
||||
|
|
||||
b order by startline, startcolumn
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(NodeRegion block, DataFlowCall call) {
|
||||
@@ -1325,7 +1296,7 @@ import IsUnreachableInCall
|
||||
* Holds if access paths with `c` at their head always should be tracked at high
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c) { none() }
|
||||
predicate forceHighPrecision(Content c) { c instanceof ElementContent }
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) {
|
||||
@@ -1364,9 +1335,9 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
predicate knownSourceModel(Node source, string model) { none() }
|
||||
predicate knownSourceModel(Node source, string model) { External::sourceNode(source, _, model) }
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { none() }
|
||||
predicate knownSinkModel(Node sink, string model) { External::sinkNode(sink, _, model) }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
@@ -1396,7 +1367,8 @@ private predicate unionHasApproxName(Cpp::Union u, string s) { s = u.getName().c
|
||||
cached
|
||||
private newtype TContentApprox =
|
||||
TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or
|
||||
TUnionApproxContent(string s) { unionHasApproxName(_, s) }
|
||||
TUnionApproxContent(string s) { unionHasApproxName(_, s) } or
|
||||
TElementApproxContent()
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox extends TContentApprox {
|
||||
@@ -1427,6 +1399,10 @@ private class UnionApproxContent extends ContentApprox, TUnionApproxContent {
|
||||
final override string toString() { result = s }
|
||||
}
|
||||
|
||||
private class ElementApproxContent extends ContentApprox, TElementApproxContent {
|
||||
final override string toString() { result = "ElementApprox" }
|
||||
}
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) {
|
||||
@@ -1441,6 +1417,9 @@ ContentApprox getContentApprox(Content c) {
|
||||
u = c.(UnionContent).getUnion() and
|
||||
unionHasApproxName(u, prefix)
|
||||
)
|
||||
or
|
||||
c instanceof ElementContent and
|
||||
result instanceof ElementApproxContent
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1700,6 +1679,14 @@ class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
|
||||
/** Gets the second-level scope containing the node `n`, if any. */
|
||||
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections to use for `ElementContent`.
|
||||
*
|
||||
* This should be equal to the largest number of stars (i.e., `*`s) in any
|
||||
* `Element` content across all of our MaD summaries, sources, and sinks.
|
||||
*/
|
||||
int getMaxElementContentIndirectionIndex() { result = 5 }
|
||||
|
||||
/**
|
||||
* Module that defines flow through iterators.
|
||||
* For example,
|
||||
@@ -1812,7 +1799,7 @@ module IteratorFlow {
|
||||
* Holds if `(bb, i)` contains a write to an iterator that may have been obtained
|
||||
* by calling `begin` (or related functions) on the variable `v`.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
certain = false and
|
||||
exists(GetsIteratorCall beginCall, Instruction writeToDeref, IRBlock bbQual, int iQual |
|
||||
isIteratorStoreInstruction(beginCall, writeToDeref) and
|
||||
@@ -1823,7 +1810,7 @@ module IteratorFlow {
|
||||
}
|
||||
|
||||
/** Holds if `(bb, i)` reads the container variable `v`. */
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
Ssa::variableRead(bb, i, v, certain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ private import SsaInternals as Ssa
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import codeql.util.Unit
|
||||
private import Node0ToString
|
||||
import ExprNodes
|
||||
|
||||
/**
|
||||
* The IR dataflow graph consists of the following nodes:
|
||||
@@ -1296,466 +1297,6 @@ class UninitializedNode extends Node {
|
||||
LocalVariable getLocalVariable() { result = v }
|
||||
}
|
||||
|
||||
private module GetConvertedResultExpression {
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
|
||||
|
||||
private Operand getAnInitializeDynamicAllocationInstructionAddress() {
|
||||
result = any(InitializeDynamicAllocationInstruction init).getAllocationAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that should be returned as the result expression from `instr`.
|
||||
*
|
||||
* Note that this predicate may return multiple results in cases where a conversion belongs to a
|
||||
* different AST element than its operand.
|
||||
*/
|
||||
Expr getConvertedResultExpression(Instruction instr, int n) {
|
||||
// Only fully converted instructions have a result for `asConvertedExpr`
|
||||
not conversionFlow(unique(Operand op |
|
||||
// The address operand of a `InitializeDynamicAllocationInstruction` is
|
||||
// special: we need to handle it during dataflow (since it's
|
||||
// effectively a store to an indirection), but it doesn't appear in
|
||||
// source syntax, so dataflow node <-> expression conversion shouldn't
|
||||
// care about it.
|
||||
op = getAUse(instr) and not op = getAnInitializeDynamicAllocationInstructionAddress()
|
||||
|
|
||||
op
|
||||
), _, false, false) and
|
||||
result = getConvertedResultExpressionImpl(instr) and
|
||||
n = 0
|
||||
or
|
||||
// If the conversion also has a result then we return multiple results
|
||||
exists(Operand operand | conversionFlow(operand, instr, false, false) |
|
||||
n = 1 and
|
||||
result = getConvertedResultExpressionImpl(operand.getDef())
|
||||
or
|
||||
result = getConvertedResultExpression(operand.getDef(), n - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl0(Instruction instr) {
|
||||
// IR construction inserts an additional cast to a `size_t` on the extent
|
||||
// of a `new[]` expression. The resulting `ConvertInstruction` doesn't have
|
||||
// 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())
|
||||
)
|
||||
or
|
||||
// There's no instruction that returns `ParenthesisExpr`, but some queries
|
||||
// expect this
|
||||
exists(TranslatedTransparentConversion ttc |
|
||||
result = ttc.getExpr().(ParenthesisExpr) and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
or
|
||||
// Certain expressions generate `CopyValueInstruction`s only when they
|
||||
// are needed. Examples of this include crement operations and compound
|
||||
// assignment operations. For example:
|
||||
// ```cpp
|
||||
// int x = ...
|
||||
// int y = x++;
|
||||
// ```
|
||||
// this generate IR like:
|
||||
// ```
|
||||
// r1(glval<int>) = VariableAddress[x] :
|
||||
// r2(int) = Constant[0] :
|
||||
// m3(int) = Store[x] : &:r1, r2
|
||||
// r4(glval<int>) = VariableAddress[y] :
|
||||
// r5(glval<int>) = VariableAddress[x] :
|
||||
// r6(int) = Load[x] : &:r5, m3
|
||||
// r7(int) = Constant[1] :
|
||||
// r8(int) = Add : r6, r7
|
||||
// m9(int) = Store[x] : &:r5, r8
|
||||
// r11(int) = CopyValue : r6
|
||||
// m12(int) = Store[y] : &:r4, r11
|
||||
// ```
|
||||
// When the `CopyValueInstruction` is not generated there is no instruction
|
||||
// whose `getConvertedResultExpression` maps back to the expression. When
|
||||
// such an instruction doesn't exist it means that the old value is not
|
||||
// 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)
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl(Instruction instr) {
|
||||
result = getConvertedResultExpressionImpl0(instr)
|
||||
or
|
||||
not exists(getConvertedResultExpressionImpl0(instr)) and
|
||||
result = instr.getConvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result for `node.asDefinition()` (when `node` is the instruction
|
||||
* node that wraps `store`) in the cases where `store.getAst()` should not be
|
||||
* used to define the result of `node.asDefinition()`.
|
||||
*/
|
||||
private Expr asDefinitionImpl0(StoreInstruction store) {
|
||||
// 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()
|
||||
)
|
||||
or
|
||||
// Similarly for `i++` and `++i` we pretend that the generated
|
||||
// `StoreInstruction` is 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()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression returned by `store.getAst()` should not be
|
||||
* returned as the result of `node.asDefinition()` when `node` is the
|
||||
* instruction node that wraps `store`.
|
||||
*/
|
||||
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())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the result of `StoreInstruction` for
|
||||
* dataflow purposes.
|
||||
*
|
||||
* For example, consider the following example
|
||||
* ```cpp
|
||||
* int x = 42; // 1
|
||||
* x = 34; // 2
|
||||
* ++x; // 3
|
||||
* x++; // 4
|
||||
* x += 1; // 5
|
||||
* int y = x += 2; // 6
|
||||
* ```
|
||||
* For (1) the result is `42`.
|
||||
* For (2) the result is `x = 34`.
|
||||
* For (3) the result is `++x`.
|
||||
* For (4) the result is `x++`.
|
||||
* For (5) the result is `x += 1`.
|
||||
* For (6) there are two results:
|
||||
* - For the `StoreInstruction` generated by `x += 2` the result
|
||||
* is `x += 2`
|
||||
* - For the `StoreInstruction` generated by `int y = ...` the result
|
||||
* is also `x += 2`
|
||||
*/
|
||||
Expr asDefinitionImpl(StoreInstruction store) {
|
||||
not exists(asDefinitionImpl0(store)) and
|
||||
not excludeAsDefinitionResult(store) and
|
||||
result = store.getAst().(Expr).getUnconverted()
|
||||
or
|
||||
result = asDefinitionImpl0(store)
|
||||
}
|
||||
}
|
||||
|
||||
private import GetConvertedResultExpression
|
||||
|
||||
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
||||
predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
||||
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||
exists(Instruction def |
|
||||
unique( | | getAUse(def)) = node.getOperand() and
|
||||
e = getConvertedResultExpression(def, n)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectOperand` that maps `node.asIndirectExpr()` to `e`. */
|
||||
private predicate indirectExprNodeShouldBeIndirectOperand(
|
||||
IndirectOperand node, Expr e, int n, int indirectionIndex
|
||||
) {
|
||||
exists(Instruction def |
|
||||
node.hasOperandAndIndirectionIndex(unique( | | getAUse(def)), indirectionIndex) and
|
||||
e = getConvertedResultExpression(def, n)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectOperand` that maps `node.asExpr()` to `e`. */
|
||||
private predicate exprNodeShouldBeIndirectOperand(IndirectOperand node, Expr e, int n) {
|
||||
exists(ArgumentOperand operand |
|
||||
// When an argument (qualifier or positional) is a prvalue and the
|
||||
// parameter (qualifier or positional) is a (const) reference, IR
|
||||
// construction introduces a temporary `IRVariable`. The `VariableAddress`
|
||||
// instruction has the argument as its `getConvertedResultExpression`
|
||||
// result. However, the instruction actually represents the _address_ of
|
||||
// the argument. So to fix this mismatch, we have the indirection of the
|
||||
// `VariableAddressInstruction` map to the expression.
|
||||
node.hasOperandAndIndirectionIndex(operand, 1) and
|
||||
e = getConvertedResultExpression(operand.getDef(), n) and
|
||||
operand.getDef().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable
|
||||
)
|
||||
}
|
||||
|
||||
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e, int n) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() instanceof Constructor and
|
||||
e = getConvertedResultExpression(call, n) and
|
||||
call.getThisArgumentOperand() = node.getAddressOperand()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an instruction node that maps `node.asExpr()` to `e`. */
|
||||
predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
||||
not exprNodeShouldBeOperand(_, e, n) and
|
||||
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
||||
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||
e = getConvertedResultExpression(node.asInstruction(), n)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */
|
||||
predicate indirectExprNodeShouldBeIndirectInstruction(
|
||||
IndirectInstruction node, Expr e, int n, int indirectionIndex
|
||||
) {
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e, n, indirectionIndex) and
|
||||
exists(Instruction instr |
|
||||
node.hasInstructionAndIndirectionIndex(instr, indirectionIndex) and
|
||||
e = getConvertedResultExpression(instr, n)
|
||||
)
|
||||
}
|
||||
|
||||
abstract private class ExprNodeBase extends Node {
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
abstract Expr getConvertedExpr(int n);
|
||||
|
||||
/** Gets the non-conversion expression corresponding to this node, if any. */
|
||||
final Expr getExpr(int n) { result = this.getConvertedExpr(n).getUnconverted() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a dataflow node whose `asExpr(n)` should evaluate
|
||||
* to `e`.
|
||||
*/
|
||||
private predicate exprNodeShouldBe(Expr e, int n) {
|
||||
exprNodeShouldBeInstruction(_, e, n) or
|
||||
exprNodeShouldBeOperand(_, e, n) or
|
||||
exprNodeShouldBeIndirectOutNode(_, e, n) or
|
||||
exprNodeShouldBeIndirectOperand(_, e, n)
|
||||
}
|
||||
|
||||
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
||||
InstructionExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeInstruction(this, e, n) and
|
||||
not exists(Expr conv |
|
||||
exprNodeShouldBe(conv, n + 1) and
|
||||
conv.getUnconverted() = e.getUnconverted()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeInstruction(this, result, n) }
|
||||
}
|
||||
|
||||
private class OperandExprNode extends ExprNodeBase, OperandNode {
|
||||
OperandExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeOperand(this, e, n) and
|
||||
not exists(Expr conv |
|
||||
exprNodeShouldBe(conv, n + 1) and
|
||||
conv.getUnconverted() = e.getUnconverted()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeOperand(this, result, n) }
|
||||
}
|
||||
|
||||
abstract private class IndirectExprNodeBase extends Node {
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
abstract Expr getConvertedExpr(int n, int indirectionIndex);
|
||||
|
||||
/** Gets the non-conversion expression corresponding to this node, if any. */
|
||||
final Expr getExpr(int n, int indirectionIndex) {
|
||||
result = this.getConvertedExpr(n, indirectionIndex).getUnconverted()
|
||||
}
|
||||
}
|
||||
|
||||
/** A signature for converting an indirect node to an expression. */
|
||||
private signature module IndirectNodeToIndirectExprSig {
|
||||
/** The indirect node class to be converted to an expression */
|
||||
class IndirectNode;
|
||||
|
||||
/**
|
||||
* Holds if the indirect expression at indirection index `indirectionIndex`
|
||||
* of `node` is `e`. The integer `n` specifies how many conversions has been
|
||||
* applied to `node`.
|
||||
*/
|
||||
predicate indirectNodeHasIndirectExpr(IndirectNode node, Expr e, int n, int indirectionIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* A module that implements the logic for deciding whether an indirect node
|
||||
* should be an `IndirectExprNode`.
|
||||
*/
|
||||
private module IndirectNodeToIndirectExpr<IndirectNodeToIndirectExprSig Sig> {
|
||||
import Sig
|
||||
|
||||
/**
|
||||
* This predicate shifts the indirection index by one when `conv` is a
|
||||
* `ReferenceDereferenceExpr`.
|
||||
*
|
||||
* This is necessary because `ReferenceDereferenceExpr` is a conversion
|
||||
* in the AST, but appears as a `LoadInstruction` in the IR.
|
||||
*/
|
||||
bindingset[e, indirectionIndex]
|
||||
private predicate adjustForReference(
|
||||
Expr e, int indirectionIndex, Expr conv, int adjustedIndirectionIndex
|
||||
) {
|
||||
conv.(ReferenceDereferenceExpr).getExpr() = e and
|
||||
adjustedIndirectionIndex = indirectionIndex - 1
|
||||
or
|
||||
not conv instanceof ReferenceDereferenceExpr and
|
||||
conv = e and
|
||||
adjustedIndirectionIndex = indirectionIndex
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectExprNode`. */
|
||||
predicate charpred(IndirectNode node) {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectNodeHasIndirectExpr(node, e, n, indirectionIndex) and
|
||||
not exists(Expr conv, int adjustedIndirectionIndex |
|
||||
adjustForReference(e, indirectionIndex, conv, adjustedIndirectionIndex) and
|
||||
indirectExprNodeShouldBe(conv, n + 1, adjustedIndirectionIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate indirectExprNodeShouldBe(Expr e, int n, int indirectionIndex) {
|
||||
indirectExprNodeShouldBeIndirectOperand(_, e, n, indirectionIndex) or
|
||||
indirectExprNodeShouldBeIndirectInstruction(_, e, n, indirectionIndex)
|
||||
}
|
||||
|
||||
private module IndirectOperandIndirectExprNodeImpl implements IndirectNodeToIndirectExprSig {
|
||||
class IndirectNode = IndirectOperand;
|
||||
|
||||
predicate indirectNodeHasIndirectExpr = indirectExprNodeShouldBeIndirectOperand/4;
|
||||
}
|
||||
|
||||
module IndirectOperandToIndirectExpr =
|
||||
IndirectNodeToIndirectExpr<IndirectOperandIndirectExprNodeImpl>;
|
||||
|
||||
private class IndirectOperandIndirectExprNode extends IndirectExprNodeBase instanceof IndirectOperand
|
||||
{
|
||||
IndirectOperandIndirectExprNode() { IndirectOperandToIndirectExpr::charpred(this) }
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
IndirectOperandToIndirectExpr::indirectNodeHasIndirectExpr(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
private module IndirectInstructionIndirectExprNodeImpl implements IndirectNodeToIndirectExprSig {
|
||||
class IndirectNode = IndirectInstruction;
|
||||
|
||||
predicate indirectNodeHasIndirectExpr = indirectExprNodeShouldBeIndirectInstruction/4;
|
||||
}
|
||||
|
||||
module IndirectInstructionToIndirectExpr =
|
||||
IndirectNodeToIndirectExpr<IndirectInstructionIndirectExprNodeImpl>;
|
||||
|
||||
private class IndirectInstructionIndirectExprNode extends IndirectExprNodeBase instanceof IndirectInstruction
|
||||
{
|
||||
IndirectInstructionIndirectExprNode() { IndirectInstructionToIndirectExpr::charpred(this) }
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
IndirectInstructionToIndirectExpr::indirectNodeHasIndirectExpr(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
private class IndirectArgumentOutExprNode extends ExprNodeBase, IndirectArgumentOutNode {
|
||||
IndirectArgumentOutExprNode() { exprNodeShouldBeIndirectOutNode(this, _, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOutNode(this, result, n) }
|
||||
}
|
||||
|
||||
private class IndirectOperandExprNode extends ExprNodeBase instanceof IndirectOperand {
|
||||
IndirectOperandExprNode() { exprNodeShouldBeIndirectOperand(this, _, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOperand(this, result, n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node instanceof ExprNodeBase {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getExpr(int n) { result = super.getExpr(n) }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression corresponding to this node, if any. If
|
||||
* this node strictly (in the sense of `getConvertedExpr`) corresponds to a
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
final Expr getExpr() { result = this.getExpr(_) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getConvertedExpr(int n) { result = super.getConvertedExpr(n) }
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
final Expr getConvertedExpr() { result = this.getConvertedExpr(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An indirect expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class IndirectExprNode extends Node instanceof IndirectExprNodeBase {
|
||||
/**
|
||||
* Gets the non-conversion expression corresponding to this node, if any. If
|
||||
* this node strictly (in the sense of `getConvertedExpr`) corresponds to a
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
final Expr getExpr(int indirectionIndex) { result = this.getExpr(_, indirectionIndex) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getExpr(int n, int indirectionIndex) { result = super.getExpr(n, indirectionIndex) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getConvertedExpr(int n, int indirectionIndex) {
|
||||
result = super.getConvertedExpr(n, indirectionIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
Expr getConvertedExpr(int indirectionIndex) {
|
||||
result = this.getConvertedExpr(_, indirectionIndex)
|
||||
}
|
||||
}
|
||||
|
||||
abstract private class AbstractParameterNode extends Node {
|
||||
/**
|
||||
* Holds if this node is the parameter of `f` at the specified position. The
|
||||
@@ -2542,6 +2083,9 @@ private newtype TContent =
|
||||
indirectionIndex =
|
||||
[1 .. max(Ssa::getMaxIndirectionsForType(getAFieldWithSize(u, bytes).getUnspecifiedType()))]
|
||||
)
|
||||
} or
|
||||
TElementContent(int indirectionIndex) {
|
||||
indirectionIndex = [1 .. getMaxElementContentIndirectionIndex()]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2652,6 +2196,25 @@ class UnionContent extends Content, TUnionContent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Content` that represents one of the elements of a
|
||||
* container (e.g., `std::vector`).
|
||||
*/
|
||||
class ElementContent extends Content, TElementContent {
|
||||
int indirectionIndex;
|
||||
|
||||
ElementContent() { this = TElementContent(indirectionIndex) }
|
||||
|
||||
pragma[inline]
|
||||
override int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
|
||||
}
|
||||
|
||||
override predicate impliesClearOf(Content c) { none() }
|
||||
|
||||
override string toString() { result = contentStars(this) + "element" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An entity that represents a set of `Content`s.
|
||||
*
|
||||
|
||||
518
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll
Normal file
518
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll
Normal file
@@ -0,0 +1,518 @@
|
||||
/**
|
||||
* Provides the classes `ExprNode` and `IndirectExprNode` for converting between `Expr` and `Node`.
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
private Operand getAnInitializeDynamicAllocationInstructionAddress() {
|
||||
result = any(InitializeDynamicAllocationInstruction init).getAllocationAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that should be returned as the result expression from `instr`.
|
||||
*
|
||||
* Note that this predicate may return multiple results in cases where a conversion belongs to a
|
||||
* different AST element than its operand.
|
||||
*/
|
||||
private Expr getConvertedResultExpression(Instruction instr, int n) {
|
||||
// Only fully converted instructions have a result for `asConvertedExpr`
|
||||
not conversionFlow(unique(Operand op |
|
||||
// The address operand of a `InitializeDynamicAllocationInstruction` is
|
||||
// special: we need to handle it during dataflow (since it's
|
||||
// effectively a store to an indirection), but it doesn't appear in
|
||||
// source syntax, so dataflow node <-> expression conversion shouldn't
|
||||
// care about it.
|
||||
op = getAUse(instr) and not op = getAnInitializeDynamicAllocationInstructionAddress()
|
||||
|
|
||||
op
|
||||
), _, false, false) and
|
||||
result = getConvertedResultExpressionImpl(instr) and
|
||||
n = 0
|
||||
or
|
||||
// If the conversion also has a result then we return multiple results
|
||||
exists(Operand operand | conversionFlow(operand, instr, false, false) |
|
||||
n = 1 and
|
||||
result = getConvertedResultExpressionImpl(operand.getDef())
|
||||
or
|
||||
result = getConvertedResultExpression(operand.getDef(), n - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl0(Instruction instr) {
|
||||
// IR construction inserts an additional cast to a `size_t` on the extent
|
||||
// of a `new[]` expression. The resulting `ConvertInstruction` doesn't have
|
||||
// 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())
|
||||
)
|
||||
or
|
||||
// There's no instruction that returns `ParenthesisExpr`, but some queries
|
||||
// expect this
|
||||
exists(TranslatedTransparentConversion ttc |
|
||||
result = ttc.getExpr().(ParenthesisExpr) and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
or
|
||||
// Certain expressions generate `CopyValueInstruction`s only when they
|
||||
// are needed. Examples of this include crement operations and compound
|
||||
// assignment operations. For example:
|
||||
// ```cpp
|
||||
// int x = ...
|
||||
// int y = x++;
|
||||
// ```
|
||||
// this generate IR like:
|
||||
// ```
|
||||
// r1(glval<int>) = VariableAddress[x] :
|
||||
// r2(int) = Constant[0] :
|
||||
// m3(int) = Store[x] : &:r1, r2
|
||||
// r4(glval<int>) = VariableAddress[y] :
|
||||
// r5(glval<int>) = VariableAddress[x] :
|
||||
// r6(int) = Load[x] : &:r5, m3
|
||||
// r7(int) = Constant[1] :
|
||||
// r8(int) = Add : r6, r7
|
||||
// m9(int) = Store[x] : &:r5, r8
|
||||
// r11(int) = CopyValue : r6
|
||||
// m12(int) = Store[y] : &:r4, r11
|
||||
// ```
|
||||
// When the `CopyValueInstruction` is not generated there is no instruction
|
||||
// whose `getConvertedResultExpression` maps back to the expression. When
|
||||
// such an instruction doesn't exist it means that the old value is not
|
||||
// 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)
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl(Instruction instr) {
|
||||
result = getConvertedResultExpressionImpl0(instr)
|
||||
or
|
||||
not exists(getConvertedResultExpressionImpl0(instr)) and
|
||||
result = instr.getConvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result for `node.asDefinition()` (when `node` is the instruction
|
||||
* node that wraps `store`) in the cases where `store.getAst()` should not be
|
||||
* used to define the result of `node.asDefinition()`.
|
||||
*/
|
||||
private Expr asDefinitionImpl0(StoreInstruction store) {
|
||||
// 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()
|
||||
)
|
||||
or
|
||||
// Similarly for `i++` and `++i` we pretend that the generated
|
||||
// `StoreInstruction` is 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()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression returned by `store.getAst()` should not be
|
||||
* returned as the result of `node.asDefinition()` when `node` is the
|
||||
* instruction node that wraps `store`.
|
||||
*/
|
||||
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())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the result of `StoreInstruction` for
|
||||
* dataflow purposes.
|
||||
*
|
||||
* For example, consider the following example
|
||||
* ```cpp
|
||||
* int x = 42; // 1
|
||||
* x = 34; // 2
|
||||
* ++x; // 3
|
||||
* x++; // 4
|
||||
* x += 1; // 5
|
||||
* int y = x += 2; // 6
|
||||
* ```
|
||||
* For (1) the result is `42`.
|
||||
* For (2) the result is `x = 34`.
|
||||
* For (3) the result is `++x`.
|
||||
* For (4) the result is `x++`.
|
||||
* For (5) the result is `x += 1`.
|
||||
* For (6) there are two results:
|
||||
* - For the `StoreInstruction` generated by `x += 2` the result
|
||||
* is `x += 2`
|
||||
* - For the `StoreInstruction` generated by `int y = ...` the result
|
||||
* is also `x += 2`
|
||||
*/
|
||||
cached
|
||||
Expr asDefinitionImpl(StoreInstruction store) {
|
||||
not exists(asDefinitionImpl0(store)) and
|
||||
not excludeAsDefinitionResult(store) and
|
||||
result = store.getAst().(Expr).getUnconverted()
|
||||
or
|
||||
result = asDefinitionImpl0(store)
|
||||
}
|
||||
|
||||
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
||||
private predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
||||
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||
exists(Instruction def |
|
||||
unique( | | getAUse(def)) = node.getOperand() and
|
||||
e = getConvertedResultExpression(def, n)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectOperand` that maps `node.asIndirectExpr()` to `e`. */
|
||||
private predicate indirectExprNodeShouldBeIndirectOperand(
|
||||
IndirectOperand node, Expr e, int n, int indirectionIndex
|
||||
) {
|
||||
exists(Instruction def |
|
||||
node.hasOperandAndIndirectionIndex(unique( | | getAUse(def)), indirectionIndex) and
|
||||
e = getConvertedResultExpression(def, n)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `operand`'s definition is a `VariableAddressInstruction` whose variable is a temporary */
|
||||
private predicate isIRTempVariable(Operand operand) {
|
||||
operand.getDef().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an indirect operand whose operand is an argument, and
|
||||
* the `n`'th expression associated with the operand is `e`.
|
||||
*/
|
||||
private predicate isIndirectOperandOfArgument(
|
||||
IndirectOperand node, ArgumentOperand operand, Expr e, int n
|
||||
) {
|
||||
node.hasOperandAndIndirectionIndex(operand, 1) and
|
||||
e = getConvertedResultExpression(operand.getDef(), n)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `opFrom` is an operand to a conversion, and `opTo` is the unique
|
||||
* use of the conversion.
|
||||
*/
|
||||
private predicate isConversionStep(Operand opFrom, Operand opTo) {
|
||||
exists(Instruction mid |
|
||||
conversionFlow(opFrom, mid, false, false) and
|
||||
opTo = unique( | | getAUse(mid))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an operand that satisfies `isIRTempVariable` flows to `op`
|
||||
* through a (possibly empty) sequence of conversions.
|
||||
*/
|
||||
private predicate irTempOperandConversionFlows(Operand op) {
|
||||
isIRTempVariable(op)
|
||||
or
|
||||
exists(Operand mid |
|
||||
irTempOperandConversionFlows(mid) and
|
||||
isConversionStep(mid, op)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectOperand` that maps `node.asExpr()` to `e`. */
|
||||
private predicate exprNodeShouldBeIndirectOperand(IndirectOperand node, Expr e, int n) {
|
||||
exists(ArgumentOperand operand |
|
||||
// When an argument (qualifier or positional) is a prvalue and the
|
||||
// parameter (qualifier or positional) is a (const) reference, IR
|
||||
// construction introduces a temporary `IRVariable`. The `VariableAddress`
|
||||
// instruction has the argument as its `getConvertedResultExpression`
|
||||
// result. However, the instruction actually represents the _address_ of
|
||||
// the argument. So to fix this mismatch, we have the indirection of the
|
||||
// `VariableAddressInstruction` map to the expression.
|
||||
isIndirectOperandOfArgument(node, operand, e, n) and
|
||||
irTempOperandConversionFlows(operand)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e, int n) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() instanceof Constructor and
|
||||
e = getConvertedResultExpression(call, n) and
|
||||
call.getThisArgumentOperand() = node.getAddressOperand()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an instruction node that maps `node.asExpr()` to `e`. */
|
||||
private predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
||||
not exprNodeShouldBeOperand(_, e, n) and
|
||||
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
||||
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||
e = getConvertedResultExpression(node.asInstruction(), n)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */
|
||||
private predicate indirectExprNodeShouldBeIndirectInstruction(
|
||||
IndirectInstruction node, Expr e, int n, int indirectionIndex
|
||||
) {
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e, n, indirectionIndex) and
|
||||
exists(Instruction instr |
|
||||
node.hasInstructionAndIndirectionIndex(instr, indirectionIndex) and
|
||||
e = getConvertedResultExpression(instr, n)
|
||||
)
|
||||
}
|
||||
|
||||
abstract private class ExprNodeBase extends Node {
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
abstract Expr getConvertedExpr(int n);
|
||||
|
||||
/** Gets the non-conversion expression corresponding to this node, if any. */
|
||||
final Expr getExpr(int n) { result = this.getConvertedExpr(n).getUnconverted() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a dataflow node whose `asExpr(n)` should evaluate
|
||||
* to `e`.
|
||||
*/
|
||||
private predicate exprNodeShouldBe(Expr e, int n) {
|
||||
exprNodeShouldBeInstruction(_, e, n) or
|
||||
exprNodeShouldBeOperand(_, e, n) or
|
||||
exprNodeShouldBeIndirectOutNode(_, e, n) or
|
||||
exprNodeShouldBeIndirectOperand(_, e, n)
|
||||
}
|
||||
|
||||
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
||||
InstructionExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeInstruction(this, e, n) and
|
||||
not exists(Expr conv |
|
||||
exprNodeShouldBe(conv, n + 1) and
|
||||
conv.getUnconverted() = e.getUnconverted()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeInstruction(this, result, n) }
|
||||
}
|
||||
|
||||
private class OperandExprNode extends ExprNodeBase, OperandNode {
|
||||
OperandExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeOperand(this, e, n) and
|
||||
not exists(Expr conv |
|
||||
exprNodeShouldBe(conv, n + 1) and
|
||||
conv.getUnconverted() = e.getUnconverted()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeOperand(this, result, n) }
|
||||
}
|
||||
|
||||
abstract private class IndirectExprNodeBase extends Node {
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
abstract Expr getConvertedExpr(int n, int indirectionIndex);
|
||||
|
||||
/** Gets the non-conversion expression corresponding to this node, if any. */
|
||||
final Expr getExpr(int n, int indirectionIndex) {
|
||||
result = this.getConvertedExpr(n, indirectionIndex).getUnconverted()
|
||||
}
|
||||
}
|
||||
|
||||
/** A signature for converting an indirect node to an expression. */
|
||||
private signature module IndirectNodeToIndirectExprSig {
|
||||
/** The indirect node class to be converted to an expression */
|
||||
class IndirectNode;
|
||||
|
||||
/**
|
||||
* Holds if the indirect expression at indirection index `indirectionIndex`
|
||||
* of `node` is `e`. The integer `n` specifies how many conversions has been
|
||||
* applied to `node`.
|
||||
*/
|
||||
predicate indirectNodeHasIndirectExpr(IndirectNode node, Expr e, int n, int indirectionIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* A module that implements the logic for deciding whether an indirect node
|
||||
* should be an `IndirectExprNode`.
|
||||
*/
|
||||
private module IndirectNodeToIndirectExpr<IndirectNodeToIndirectExprSig Sig> {
|
||||
import Sig
|
||||
|
||||
/**
|
||||
* This predicate shifts the indirection index by one when `conv` is a
|
||||
* `ReferenceDereferenceExpr`.
|
||||
*
|
||||
* This is necessary because `ReferenceDereferenceExpr` is a conversion
|
||||
* in the AST, but appears as a `LoadInstruction` in the IR.
|
||||
*/
|
||||
bindingset[e, indirectionIndex]
|
||||
private predicate adjustForReference(
|
||||
Expr e, int indirectionIndex, Expr conv, int adjustedIndirectionIndex
|
||||
) {
|
||||
conv.(ReferenceDereferenceExpr).getExpr() = e and
|
||||
adjustedIndirectionIndex = indirectionIndex - 1
|
||||
or
|
||||
not conv instanceof ReferenceDereferenceExpr and
|
||||
conv = e and
|
||||
adjustedIndirectionIndex = indirectionIndex
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectExprNode`. */
|
||||
predicate charpred(IndirectNode node) {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectNodeHasIndirectExpr(node, e, n, indirectionIndex) and
|
||||
not exists(Expr conv, int adjustedIndirectionIndex |
|
||||
adjustForReference(e, indirectionIndex, conv, adjustedIndirectionIndex) and
|
||||
indirectExprNodeShouldBe(conv, n + 1, adjustedIndirectionIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate indirectExprNodeShouldBe(Expr e, int n, int indirectionIndex) {
|
||||
indirectExprNodeShouldBeIndirectOperand(_, e, n, indirectionIndex) or
|
||||
indirectExprNodeShouldBeIndirectInstruction(_, e, n, indirectionIndex)
|
||||
}
|
||||
|
||||
private module IndirectOperandIndirectExprNodeImpl implements IndirectNodeToIndirectExprSig {
|
||||
class IndirectNode = IndirectOperand;
|
||||
|
||||
predicate indirectNodeHasIndirectExpr = indirectExprNodeShouldBeIndirectOperand/4;
|
||||
}
|
||||
|
||||
module IndirectOperandToIndirectExpr =
|
||||
IndirectNodeToIndirectExpr<IndirectOperandIndirectExprNodeImpl>;
|
||||
|
||||
private class IndirectOperandIndirectExprNode extends IndirectExprNodeBase instanceof IndirectOperand
|
||||
{
|
||||
IndirectOperandIndirectExprNode() { IndirectOperandToIndirectExpr::charpred(this) }
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
IndirectOperandToIndirectExpr::indirectNodeHasIndirectExpr(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
private module IndirectInstructionIndirectExprNodeImpl implements IndirectNodeToIndirectExprSig {
|
||||
class IndirectNode = IndirectInstruction;
|
||||
|
||||
predicate indirectNodeHasIndirectExpr = indirectExprNodeShouldBeIndirectInstruction/4;
|
||||
}
|
||||
|
||||
module IndirectInstructionToIndirectExpr =
|
||||
IndirectNodeToIndirectExpr<IndirectInstructionIndirectExprNodeImpl>;
|
||||
|
||||
private class IndirectInstructionIndirectExprNode extends IndirectExprNodeBase instanceof IndirectInstruction
|
||||
{
|
||||
IndirectInstructionIndirectExprNode() { IndirectInstructionToIndirectExpr::charpred(this) }
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
IndirectInstructionToIndirectExpr::indirectNodeHasIndirectExpr(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
private class IndirectArgumentOutExprNode extends ExprNodeBase, IndirectArgumentOutNode {
|
||||
IndirectArgumentOutExprNode() { exprNodeShouldBeIndirectOutNode(this, _, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOutNode(this, result, n) }
|
||||
}
|
||||
|
||||
private class IndirectOperandExprNode extends ExprNodeBase instanceof IndirectOperand {
|
||||
IndirectOperandExprNode() { exprNodeShouldBeIndirectOperand(this, _, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOperand(this, result, n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
cached
|
||||
class ExprNode extends Node instanceof ExprNodeBase {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
cached
|
||||
Expr getExpr(int n) { result = super.getExpr(n) }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression corresponding to this node, if any. If
|
||||
* this node strictly (in the sense of `getConvertedExpr`) corresponds to a
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
cached
|
||||
final Expr getExpr() { result = this.getExpr(_) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
cached
|
||||
Expr getConvertedExpr(int n) { result = super.getConvertedExpr(n) }
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
cached
|
||||
final Expr getConvertedExpr() { result = this.getConvertedExpr(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An indirect expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
cached
|
||||
class IndirectExprNode extends Node instanceof IndirectExprNodeBase {
|
||||
/**
|
||||
* Gets the non-conversion expression corresponding to this node, if any. If
|
||||
* this node strictly (in the sense of `getConvertedExpr`) corresponds to a
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
cached
|
||||
final Expr getExpr(int indirectionIndex) { result = this.getExpr(_, indirectionIndex) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
cached
|
||||
Expr getExpr(int n, int indirectionIndex) { result = super.getExpr(n, indirectionIndex) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
cached
|
||||
Expr getConvertedExpr(int n, int indirectionIndex) {
|
||||
result = super.getConvertedExpr(n, indirectionIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
cached
|
||||
Expr getConvertedExpr(int indirectionIndex) {
|
||||
result = this.getConvertedExpr(_, indirectionIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -104,7 +104,7 @@ predicate hasRawIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
|
||||
cached
|
||||
private newtype TDefImpl =
|
||||
TDefAddressImpl(BaseIRVariable v) or
|
||||
TDefAddressImpl(BaseSourceVariable v) or
|
||||
TDirectDefImpl(Operand address, int indirectionIndex) {
|
||||
isDef(_, _, address, _, _, indirectionIndex)
|
||||
} or
|
||||
@@ -325,9 +325,9 @@ private Instruction getInitializationTargetAddress(IRVariable v) {
|
||||
)
|
||||
}
|
||||
|
||||
/** An initial definition of an `IRVariable`'s address. */
|
||||
private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||
BaseIRVariable v;
|
||||
/** An initial definition of an SSA variable address. */
|
||||
abstract private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||
BaseSourceVariable v;
|
||||
|
||||
DefAddressImpl() {
|
||||
this = TDefAddressImpl(v) and
|
||||
@@ -342,6 +342,19 @@ private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||
|
||||
final override Node0Impl getValue() { none() }
|
||||
|
||||
override Cpp::Location getLocation() { result = v.getLocation() }
|
||||
|
||||
final override SourceVariable getSourceVariable() {
|
||||
result.getBaseVariable() = v and
|
||||
result.getIndirection() = 0
|
||||
}
|
||||
|
||||
final override BaseSourceVariable getBaseSourceVariable() { result = v }
|
||||
}
|
||||
|
||||
private class DefVariableAddressImpl extends DefAddressImpl {
|
||||
override BaseIRVariable v;
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
exists(IRVariable var | var = v.getIRVariable() |
|
||||
block.getInstruction(index) = getInitializationTargetAddress(var)
|
||||
@@ -353,15 +366,14 @@ private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||
index = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override Cpp::Location getLocation() { result = v.getIRVariable().getLocation() }
|
||||
private class DefCallAddressImpl extends DefAddressImpl {
|
||||
override BaseCallVariable v;
|
||||
|
||||
final override SourceVariable getSourceVariable() {
|
||||
result.getBaseVariable() = v and
|
||||
result.getIndirection() = 0
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
block.getInstruction(index) = v.getCallInstruction()
|
||||
}
|
||||
|
||||
final override BaseSourceVariable getBaseSourceVariable() { result = v }
|
||||
}
|
||||
|
||||
private class DirectDef extends DefImpl, TDirectDefImpl {
|
||||
@@ -981,7 +993,7 @@ private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
@@ -999,7 +1011,7 @@ private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed. For C++, this is always the case.
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
|
||||
if use.isCertain() then certain = true else certain = false
|
||||
)
|
||||
|
||||
@@ -40,7 +40,8 @@ predicate ignoreInstruction(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction or
|
||||
instr instanceof AliasedUseInstruction or
|
||||
instr instanceof InitializeNonLocalInstruction or
|
||||
instr instanceof ReturnIndirectionInstruction
|
||||
instr instanceof ReturnIndirectionInstruction or
|
||||
instr instanceof UninitializedGroupInstruction
|
||||
)
|
||||
}
|
||||
|
||||
@@ -757,13 +758,19 @@ import Cached
|
||||
* between the SSA pruning stage, and the final SSA stage.
|
||||
*/
|
||||
module InputSigCommon {
|
||||
class BasicBlock = IRBlock;
|
||||
class BasicBlock extends IRBlock {
|
||||
ControlFlowNode getNode(int i) { result = this.getInstruction(i) }
|
||||
|
||||
int length() { result = this.getInstructionCount() }
|
||||
}
|
||||
|
||||
class ControlFlowNode = Instruction;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends IRBlock {
|
||||
class ExitBasicBlock extends BasicBlock {
|
||||
ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,10 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink, st
|
||||
* of `c` at sinks and inputs to additional taint steps.
|
||||
*/
|
||||
bindingset[node]
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() }
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
node instanceof ArgumentNode and
|
||||
c.isSingleton(any(ElementContent ec))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
|
||||
@@ -13,7 +13,8 @@ private newtype TMemoryAccessKind =
|
||||
TPhiMemoryAccess() or
|
||||
TUnmodeledMemoryAccess() or
|
||||
TChiTotalMemoryAccess() or
|
||||
TChiPartialMemoryAccess()
|
||||
TChiPartialMemoryAccess() or
|
||||
TGroupedMemoryAccess()
|
||||
|
||||
/**
|
||||
* Describes the set of memory locations memory accessed by a memory operand or
|
||||
@@ -99,3 +100,11 @@ class ChiTotalMemoryAccess extends MemoryAccessKind, TChiTotalMemoryAccess {
|
||||
class ChiPartialMemoryAccess extends MemoryAccessKind, TChiPartialMemoryAccess {
|
||||
override string toString() { result = "chi(partial)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of an `UninitializedGroup` instruction, which initializes a set of
|
||||
* allocations that are each assigned the same virtual variable.
|
||||
*/
|
||||
class GroupedMemoryAccess extends MemoryAccessKind, TGroupedMemoryAccess {
|
||||
override string toString() { result = "group" }
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ private newtype TOpcode =
|
||||
TSizedBufferMayWriteSideEffect() or
|
||||
TInitializeDynamicAllocation() or
|
||||
TChi() or
|
||||
TUninitializedGroup() or
|
||||
TInlineAsm() or
|
||||
TUnreached() or
|
||||
TNewObj()
|
||||
@@ -1237,6 +1238,17 @@ module Opcode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Opcode` for a `UninitializedGroup`.
|
||||
*
|
||||
* See the `UninitializedGroupInstruction` documentation for more details.
|
||||
*/
|
||||
class UninitializedGroup extends Opcode, TUninitializedGroup {
|
||||
final override string toString() { result = "UninitializedGroup" }
|
||||
|
||||
override GroupedMemoryAccess getWriteMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Opcode` for an `InlineAsmInstruction`.
|
||||
*
|
||||
|
||||
@@ -2142,6 +2142,47 @@ class ChiInstruction extends Instruction {
|
||||
final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes a set of allocations that are each assigned
|
||||
* the same "virtual variable".
|
||||
*
|
||||
* As an example, consider the following snippet:
|
||||
* ```
|
||||
* int a;
|
||||
* int b;
|
||||
* int* p;
|
||||
* if(b) {
|
||||
* p = &a;
|
||||
* } else {
|
||||
* p = &b;
|
||||
* }
|
||||
* *p = 5;
|
||||
* int x = a;
|
||||
* ```
|
||||
*
|
||||
* Since both the address of `a` and `b` reach `p` at `*p = 5` the IR alias
|
||||
* analysis will create a region that contains both `a` and `b`. The region
|
||||
* containing both `a` and `b` are initialized by an `UninitializedGroup`
|
||||
* instruction in the entry block of the enclosing function.
|
||||
*/
|
||||
class UninitializedGroupInstruction extends Instruction {
|
||||
UninitializedGroupInstruction() { this.getOpcode() instanceof Opcode::UninitializedGroup }
|
||||
|
||||
/**
|
||||
* Gets an `IRVariable` whose memory is initialized by this instruction, if any.
|
||||
* Note: Allocations that are not represented as `IRVariable`s (such as
|
||||
* dynamic allocations) are not returned by this predicate even if this
|
||||
* instruction initializes such memory.
|
||||
*/
|
||||
final IRVariable getAnIRVariable() {
|
||||
result = Construction::getAnUninitializedGroupVariable(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
result = strictconcat(this.getAnIRVariable().toString(), ",")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code.
|
||||
*
|
||||
|
||||
@@ -106,8 +106,7 @@ private predicate operandEscapesDomain(Operand operand) {
|
||||
not isArgumentForParameter(_, operand, _) and
|
||||
not isOnlyEscapesViaReturnArgument(operand) and
|
||||
not operand.getUse() instanceof ReturnValueInstruction and
|
||||
not operand.getUse() instanceof ReturnIndirectionInstruction and
|
||||
not operand instanceof PhiInputOperand
|
||||
not operand.getUse() instanceof ReturnIndirectionInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,6 +190,11 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instr
|
||||
// A copy propagates the source value.
|
||||
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
|
||||
)
|
||||
or
|
||||
operand = instr.(PhiInstruction).getAnInputOperand() and
|
||||
// Using `unknown` ensures termination since we cannot keep incrementing a bit offset
|
||||
// through the back edge of a loop (or through recursion).
|
||||
bitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
private predicate operandEscapesNonReturn(Operand operand) {
|
||||
@@ -212,9 +216,6 @@ private predicate operandEscapesNonReturn(Operand operand) {
|
||||
or
|
||||
isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUse())
|
||||
or
|
||||
operand instanceof PhiInputOperand and
|
||||
resultEscapesNonReturn(operand.getUse())
|
||||
or
|
||||
operandEscapesDomain(operand)
|
||||
}
|
||||
|
||||
@@ -236,9 +237,6 @@ private predicate operandMayReachReturn(Operand operand) {
|
||||
operand.getUse() instanceof ReturnValueInstruction
|
||||
or
|
||||
isOnlyEscapesViaReturnArgument(operand) and resultMayReachReturn(operand.getUse())
|
||||
or
|
||||
operand instanceof PhiInputOperand and
|
||||
resultMayReachReturn(operand.getUse())
|
||||
}
|
||||
|
||||
private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||
@@ -340,6 +338,56 @@ private predicate resultEscapesNonReturn(Instruction instr) {
|
||||
not instr.isResultModeled()
|
||||
}
|
||||
|
||||
/** Holds if `operand` may (transitively) flow to an `AddressOperand`. */
|
||||
private predicate consumedAsAddressOperand(Operand operand) {
|
||||
operand instanceof AddressOperand
|
||||
or
|
||||
exists(Operand address |
|
||||
consumedAsAddressOperand(address) and
|
||||
operandIsPropagated(operand, _, address.getDef())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operand` may originate from a base instruction of an allocation,
|
||||
* and that operand may transitively flow to an `AddressOperand`.
|
||||
*/
|
||||
private predicate propagatedFromAllocationBase(Operand operand, Configuration::Allocation allocation) {
|
||||
consumedAsAddressOperand(operand) and
|
||||
(
|
||||
not exists(Configuration::getOldAllocation(allocation)) and
|
||||
operand.getDef() = allocation.getABaseInstruction()
|
||||
or
|
||||
exists(Operand address |
|
||||
operandIsPropagated(address, _, operand.getDef()) and
|
||||
propagatedFromAllocationBase(address, allocation)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate propagatedFromNonAllocationBase(Operand operand) {
|
||||
exists(Instruction def |
|
||||
def = operand.getDef() and
|
||||
not operandIsPropagated(_, _, def) and
|
||||
not def = any(Configuration::Allocation allocation).getABaseInstruction()
|
||||
)
|
||||
or
|
||||
exists(Operand address |
|
||||
operandIsPropagated(address, _, operand.getDef()) and
|
||||
propagatedFromNonAllocationBase(address)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we cannot see all producers of an operand for which allocation also flows into.
|
||||
*/
|
||||
private predicate operandConsumesEscaped(Configuration::Allocation allocation) {
|
||||
exists(AddressOperand address |
|
||||
propagatedFromAllocationBase(address, allocation) and
|
||||
propagatedFromNonAllocationBase(address)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the address of `allocation` escapes outside the domain of the analysis. This can occur
|
||||
* either because the allocation's address is taken within the function and escapes, or because the
|
||||
@@ -348,12 +396,14 @@ private predicate resultEscapesNonReturn(Instruction instr) {
|
||||
predicate allocationEscapes(Configuration::Allocation allocation) {
|
||||
allocation.alwaysEscapes()
|
||||
or
|
||||
exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() |
|
||||
resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
or
|
||||
operandConsumesEscaped(allocation)
|
||||
)
|
||||
or
|
||||
Configuration::phaseNeedsSoundEscapeAnalysis() and
|
||||
resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
(resultEscapesNonReturn(allocation.getABaseInstruction()) or operandConsumesEscaped(allocation))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,6 +101,8 @@ class IndirectParameterAllocation extends Allocation, TIndirectParameterAllocati
|
||||
final override predicate isAlwaysAllocatedOnStack() { none() }
|
||||
|
||||
final override predicate alwaysEscapes() { none() }
|
||||
|
||||
final IRAutomaticVariable getIRVariable() { result = var }
|
||||
}
|
||||
|
||||
class DynamicAllocation extends Allocation, TDynamicAllocation {
|
||||
@@ -144,3 +146,8 @@ class DynamicAllocation extends Allocation, TDynamicAllocation {
|
||||
}
|
||||
|
||||
predicate phaseNeedsSoundEscapeAnalysis() { none() }
|
||||
|
||||
UnaliasedSsa::Allocation getOldAllocation(VariableAllocation allocation) {
|
||||
UnaliasedSsa::canReuseSsaForVariable(allocation.getIRVariable()) and
|
||||
result = allocation.getIRVariable()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConst
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
private import semmle.code.cpp.ir.internal.IntegerInterval as Interval
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import AliasConfiguration
|
||||
import AliasConfiguration
|
||||
private import codeql.util.Boolean
|
||||
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
@@ -16,49 +17,196 @@ private predicate isIndirectOrBufferMemoryAccess(MemoryAccessKind kind) {
|
||||
kind instanceof BufferMemoryAccess
|
||||
}
|
||||
|
||||
private predicate hasResultMemoryAccess(
|
||||
Instruction instr, Allocation var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess
|
||||
private predicate hasMemoryAccess(
|
||||
AddressOperand addrOperand, Allocation var, IntValue startBitOffset, boolean grouped
|
||||
) {
|
||||
exists(AddressOperand addrOperand |
|
||||
addrOperand = instr.getResultAddressOperand() and
|
||||
addressOperandAllocationAndOffset(addrOperand, var, startBitOffset) and
|
||||
languageType = instr.getResultLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
isIndirectOrBufferMemoryAccess(instr.getResultMemoryAccess()) and
|
||||
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
)
|
||||
addressOperandAllocationAndOffset(addrOperand, var, startBitOffset) and
|
||||
if strictcount(Allocation alloc | addressOperandAllocationAndOffset(addrOperand, alloc, _)) > 1
|
||||
then grouped = true
|
||||
else grouped = false
|
||||
}
|
||||
|
||||
private predicate hasResultMemoryAccess(
|
||||
AddressOperand address, Instruction instr, Allocation var, IRType type,
|
||||
Language::LanguageType languageType, IntValue startBitOffset, IntValue endBitOffset,
|
||||
boolean isMayAccess, boolean grouped
|
||||
) {
|
||||
address = instr.getResultAddressOperand() and
|
||||
hasMemoryAccess(address, var, startBitOffset, grouped) and
|
||||
languageType = instr.getResultLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
isIndirectOrBufferMemoryAccess(instr.getResultMemoryAccess()) and
|
||||
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
private predicate hasOperandMemoryAccess(
|
||||
MemoryOperand operand, Allocation var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess
|
||||
AddressOperand address, MemoryOperand operand, Allocation var, IRType type,
|
||||
Language::LanguageType languageType, IntValue startBitOffset, IntValue endBitOffset,
|
||||
boolean isMayAccess, boolean grouped
|
||||
) {
|
||||
exists(AddressOperand addrOperand |
|
||||
addrOperand = operand.getAddressOperand() and
|
||||
addressOperandAllocationAndOffset(addrOperand, var, startBitOffset) and
|
||||
languageType = operand.getLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
isIndirectOrBufferMemoryAccess(operand.getMemoryAccess()) and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
address = operand.getAddressOperand() and
|
||||
hasMemoryAccess(address, var, startBitOffset, grouped) and
|
||||
languageType = operand.getLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
isIndirectOrBufferMemoryAccess(operand.getMemoryAccess()) and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
private Allocation getAnAllocation(AddressOperand address) {
|
||||
hasResultMemoryAccess(address, _, result, _, _, _, _, _, true) or
|
||||
hasOperandMemoryAccess(address, _, result, _, _, _, _, _, true)
|
||||
}
|
||||
|
||||
private module AllocationSet0 =
|
||||
QlBuiltins::InternSets<AddressOperand, Allocation, getAnAllocation/1>;
|
||||
|
||||
/**
|
||||
* A set of allocations containing at least 2 elements.
|
||||
*/
|
||||
private class NonSingletonSets extends AllocationSet0::Set {
|
||||
NonSingletonSets() { strictcount(Allocation var | this.contains(var)) > 1 }
|
||||
|
||||
/** Gets an allocation from this set. */
|
||||
Allocation getAnAllocation() { this.contains(result) }
|
||||
|
||||
/** Gets the string representation of this set. */
|
||||
string toString() { result = "{" + strictconcat(this.getAnAllocation().toString(), ", ") + "}" }
|
||||
}
|
||||
|
||||
/** Holds the instersection of `s1` and `s2` is non-empty. */
|
||||
private predicate hasOverlappingElement(NonSingletonSets s1, NonSingletonSets s2) {
|
||||
exists(Allocation var |
|
||||
s1.contains(var) and
|
||||
s2.contains(var)
|
||||
)
|
||||
}
|
||||
|
||||
private module AllocationSet =
|
||||
QlBuiltins::EquivalenceRelation<NonSingletonSets, hasOverlappingElement/2>;
|
||||
|
||||
/**
|
||||
* Holds if `var` is created by the AST element `e`. Furthermore, the value `d`
|
||||
* represents which branch of the `Allocation` type `var` is from.
|
||||
*/
|
||||
private predicate allocationAst(Allocation var, @element e, int d) {
|
||||
var.(VariableAllocation).getIRVariable().getAst() = e and d = 0
|
||||
or
|
||||
var.(IndirectParameterAllocation).getIRVariable().getAst() = e and d = 1
|
||||
or
|
||||
var.(DynamicAllocation).getABaseInstruction().getAst() = e and d = 2
|
||||
}
|
||||
|
||||
/** Holds if `x = y` and `x` is an AST element that creates an `Allocation`. */
|
||||
private predicate id(@element x, @element y) {
|
||||
allocationAst(_, x, _) and
|
||||
x = y
|
||||
}
|
||||
|
||||
private predicate idOf(@element x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
|
||||
/** Gets a unique integer representation of `var`. */
|
||||
private int getUniqueAllocationId(Allocation var) {
|
||||
exists(int r, @element e, int d |
|
||||
allocationAst(var, e, d) and
|
||||
idOf(e, r) and
|
||||
result = 3 * r + d
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An equivalence class of a set of allocations.
|
||||
*
|
||||
* Any `VariableGroup` will be completely disjunct from any other
|
||||
* `VariableGroup`.
|
||||
*/
|
||||
class VariableGroup extends AllocationSet::EquivalenceClass {
|
||||
/** Gets the location of this set. */
|
||||
final Location getLocation() { result = this.getIRFunction().getLocation() }
|
||||
|
||||
/** Gets the enclosing `IRFunction` of this set. */
|
||||
final IRFunction getIRFunction() {
|
||||
result = unique( | | this.getAnAllocation().getEnclosingIRFunction())
|
||||
}
|
||||
|
||||
/** Gets the type of elements contained in this set. */
|
||||
final Language::LanguageType getType() {
|
||||
strictcount(Language::LanguageType langType |
|
||||
exists(Allocation var | var = this.getAnAllocation() |
|
||||
hasResultMemoryAccess(_, _, var, _, langType, _, _, _, true) or
|
||||
hasOperandMemoryAccess(_, _, var, _, langType, _, _, _, true)
|
||||
)
|
||||
) = 1 and
|
||||
exists(Allocation var | var = this.getAnAllocation() |
|
||||
hasResultMemoryAccess(_, _, var, _, result, _, _, _, true) or
|
||||
hasOperandMemoryAccess(_, _, var, _, result, _, _, _, true)
|
||||
)
|
||||
or
|
||||
strictcount(Language::LanguageType langType |
|
||||
exists(Allocation var | var = this.getAnAllocation() |
|
||||
hasResultMemoryAccess(_, _, var, _, langType, _, _, _, true) or
|
||||
hasOperandMemoryAccess(_, _, var, _, langType, _, _, _, true)
|
||||
)
|
||||
) > 1 and
|
||||
result = any(IRUnknownType type).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
/** Gets an allocation of this set. */
|
||||
final Allocation getAnAllocation() {
|
||||
exists(AllocationSet0::Set set |
|
||||
this = AllocationSet::getEquivalenceClass(set) and
|
||||
set.contains(result)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a unique string representing this set. */
|
||||
final private string getUniqueId() {
|
||||
result = strictconcat(getUniqueAllocationId(this.getAnAllocation()).toString(), ",")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the order that this set should be initialized in.
|
||||
*
|
||||
* Note: This is _not_ the order in which the _members_ of the set should be
|
||||
* initialized. Rather, it represents the order in which the set should be
|
||||
* initialized in relation to other sets. That is, if
|
||||
* ```
|
||||
* getInitializationOrder() = 2
|
||||
* ```
|
||||
* then this set will be initialized as the second (third) set in the
|
||||
* enclosing function. In order words, the third `UninitializedGroup`
|
||||
* instruction in the entry block of the enclosing function will initialize
|
||||
* this set of allocations.
|
||||
*/
|
||||
final int getInitializationOrder() {
|
||||
exists(IRFunction func |
|
||||
func = this.getIRFunction() and
|
||||
this =
|
||||
rank[result + 1](VariableGroup vg, string uniq |
|
||||
vg.getIRFunction() = func and uniq = vg.getUniqueId()
|
||||
|
|
||||
vg order by uniq
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
string toString() { result = "{" + strictconcat(this.getAnAllocation().toString(), ", ") + "}" }
|
||||
}
|
||||
|
||||
private newtype TMemoryLocation =
|
||||
TVariableMemoryLocation(
|
||||
Allocation var, IRType type, Language::LanguageType languageType, IntValue startBitOffset,
|
||||
IntValue endBitOffset, boolean isMayAccess
|
||||
) {
|
||||
(
|
||||
hasResultMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
hasResultMemoryAccess(_, _, var, type, _, startBitOffset, endBitOffset, isMayAccess, false)
|
||||
or
|
||||
hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
hasOperandMemoryAccess(_, _, var, type, _, startBitOffset, endBitOffset, isMayAccess, false)
|
||||
or
|
||||
// For a stack variable, always create a memory location for the entire variable.
|
||||
var.isAlwaysAllocatedOnStack() and
|
||||
@@ -69,31 +217,25 @@ private newtype TMemoryLocation =
|
||||
) and
|
||||
languageType = type.getCanonicalLanguageType()
|
||||
} or
|
||||
TEntireAllocationMemoryLocation(Allocation var, boolean isMayAccess) {
|
||||
(
|
||||
var instanceof IndirectParameterAllocation or
|
||||
var instanceof DynamicAllocation
|
||||
) and
|
||||
(isMayAccess = false or isMayAccess = true)
|
||||
TEntireAllocationMemoryLocation(Allocation var, Boolean isMayAccess) {
|
||||
var instanceof IndirectParameterAllocation or
|
||||
var instanceof DynamicAllocation
|
||||
} or
|
||||
TUnknownMemoryLocation(IRFunction irFunc, boolean isMayAccess) {
|
||||
isMayAccess = false or isMayAccess = true
|
||||
} or
|
||||
TAllNonLocalMemory(IRFunction irFunc, boolean isMayAccess) {
|
||||
isMayAccess = false or isMayAccess = true
|
||||
} or
|
||||
TAllAliasedMemory(IRFunction irFunc, boolean isMayAccess) {
|
||||
isMayAccess = false or isMayAccess = true
|
||||
}
|
||||
TGroupedMemoryLocation(VariableGroup vg, Boolean isMayAccess, Boolean isAll) or
|
||||
TUnknownMemoryLocation(IRFunction irFunc, Boolean isMayAccess) or
|
||||
TAllNonLocalMemory(IRFunction irFunc, Boolean isMayAccess) or
|
||||
TAllAliasedMemory(IRFunction irFunc, Boolean isMayAccess)
|
||||
|
||||
/**
|
||||
* Represents the memory location accessed by a memory operand or memory result. In this implementation, the location is
|
||||
* A memory location accessed by a memory operand or memory result. In this implementation, the location is
|
||||
* one of the following:
|
||||
* - `VariableMemoryLocation` - A location within a known `IRVariable`, at an offset that is either a constant or is
|
||||
* unknown.
|
||||
* - `UnknownMemoryLocation` - A location not known to be within a specific `IRVariable`.
|
||||
*
|
||||
* Some of these memory locations will be filtered out for performance reasons before being passed to SSA construction.
|
||||
*/
|
||||
abstract class MemoryLocation extends TMemoryLocation {
|
||||
abstract private class MemoryLocation0 extends TMemoryLocation {
|
||||
final string toString() {
|
||||
if this.isMayAccess()
|
||||
then result = "?" + this.toStringInternal()
|
||||
@@ -116,7 +258,14 @@ abstract class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
abstract predicate isMayAccess();
|
||||
|
||||
Allocation getAllocation() { none() }
|
||||
/**
|
||||
* Gets an allocation associated with this `MemoryLocation`.
|
||||
*
|
||||
* This returns zero or one results in all cases except when `this` is an
|
||||
* instance of `GroupedMemoryLocation`. When `this` is an instance of
|
||||
* `GroupedMemoryLocation` this predicate always returns two or more results.
|
||||
*/
|
||||
Allocation getAnAllocation() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the location cannot be overwritten except by definition of a `MemoryLocation` for
|
||||
@@ -147,30 +296,35 @@ abstract class MemoryLocation extends TMemoryLocation {
|
||||
* represented by a `MemoryLocation` that totally overlaps all other
|
||||
* `MemoryLocations` in the set.
|
||||
*/
|
||||
abstract class VirtualVariable extends MemoryLocation { }
|
||||
abstract class VirtualVariable extends MemoryLocation0 { }
|
||||
|
||||
abstract class AllocationMemoryLocation extends MemoryLocation {
|
||||
abstract class AllocationMemoryLocation extends MemoryLocation0 {
|
||||
Allocation var;
|
||||
boolean isMayAccess;
|
||||
|
||||
AllocationMemoryLocation() {
|
||||
this instanceof TMemoryLocation and
|
||||
isMayAccess = false
|
||||
or
|
||||
isMayAccess = true // Just ensures that `isMayAccess` is bound.
|
||||
}
|
||||
bindingset[isMayAccess]
|
||||
AllocationMemoryLocation() { any() }
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
if allocationEscapes(var)
|
||||
then result = TAllAliasedMemory(var.getEnclosingIRFunction(), false)
|
||||
else result.(AllocationMemoryLocation).getAllocation() = var
|
||||
else (
|
||||
// It may be that the grouped memory location contains an escaping
|
||||
// allocation. In that case, the virtual variable is still the memory
|
||||
// location that represents all aliased memory. Thus, we need to
|
||||
// call `getVirtualVariable` on the grouped memory location.
|
||||
result = getGroupedMemoryLocation(var, false, false).getVirtualVariable()
|
||||
or
|
||||
not exists(getGroupedMemoryLocation(var, false, false)) and
|
||||
result.(AllocationMemoryLocation).getAnAllocation() = var
|
||||
)
|
||||
}
|
||||
|
||||
final override IRFunction getIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final override Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final override Allocation getAllocation() { result = var }
|
||||
final override Allocation getAnAllocation() { result = var }
|
||||
|
||||
final override predicate isMayAccess() { isMayAccess = true }
|
||||
|
||||
@@ -211,13 +365,13 @@ class VariableMemoryLocation extends TVariableMemoryLocation, AllocationMemoryLo
|
||||
final override Language::LanguageType getType() {
|
||||
if
|
||||
strictcount(Language::LanguageType accessType |
|
||||
hasResultMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset, _) or
|
||||
hasOperandMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset, _)
|
||||
hasResultMemoryAccess(_, _, var, type, accessType, startBitOffset, endBitOffset, _, false) or
|
||||
hasOperandMemoryAccess(_, _, var, type, accessType, startBitOffset, endBitOffset, _, false)
|
||||
) = 1
|
||||
then
|
||||
// All of the accesses have the same `LanguageType`, so just use that.
|
||||
hasResultMemoryAccess(_, var, type, result, startBitOffset, endBitOffset, _) or
|
||||
hasOperandMemoryAccess(_, var, type, result, startBitOffset, endBitOffset, _)
|
||||
hasResultMemoryAccess(_, _, var, type, result, startBitOffset, endBitOffset, _, false) or
|
||||
hasOperandMemoryAccess(_, _, var, type, result, startBitOffset, endBitOffset, _, false)
|
||||
else
|
||||
// There is no single type for all accesses, so just use the canonical one for this `IRType`.
|
||||
result = type.getCanonicalLanguageType()
|
||||
@@ -247,6 +401,89 @@ class VariableMemoryLocation extends TVariableMemoryLocation, AllocationMemoryLo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A group of allocations represented as a single memory location.
|
||||
*
|
||||
* If `isAll()` holds then this memory location represents all the enclosing
|
||||
* allocations, and if `isSome()` holds then this memory location represents
|
||||
* one or more of the enclosing allocations.
|
||||
*
|
||||
* For example, consider the following snippet:
|
||||
* ```
|
||||
* int* p;
|
||||
* int a, b;
|
||||
* if(b) {
|
||||
* p = &a;
|
||||
* } else {
|
||||
* p = &b;
|
||||
* }
|
||||
* *p = 42;
|
||||
* ```
|
||||
*
|
||||
* The write memory location associated with the write to `*p` writes to a
|
||||
* grouped memory location representing the _some_ allocation in the set
|
||||
* `{a, b}`, and the subsequent `Chi` instruction merges the new value of
|
||||
* `{a, b}` into a memory location that represents _all_ of the allocations
|
||||
* in the set.
|
||||
*/
|
||||
class GroupedMemoryLocation extends TGroupedMemoryLocation, MemoryLocation0 {
|
||||
VariableGroup vg;
|
||||
boolean isMayAccess;
|
||||
boolean isAll;
|
||||
|
||||
GroupedMemoryLocation() { this = TGroupedMemoryLocation(vg, isMayAccess, isAll) }
|
||||
|
||||
final override Location getLocation() { result = vg.getLocation() }
|
||||
|
||||
final override IRFunction getIRFunction() { result = vg.getIRFunction() }
|
||||
|
||||
final override predicate isMayAccess() { isMayAccess = true }
|
||||
|
||||
final override string getUniqueId() {
|
||||
if this.isAll()
|
||||
then result = "All{" + strictconcat(vg.getAnAllocation().getUniqueId(), ", ") + "}"
|
||||
else result = "Some{" + strictconcat(vg.getAnAllocation().getUniqueId(), ", ") + "}"
|
||||
}
|
||||
|
||||
final override string toStringInternal() { result = this.getUniqueId() }
|
||||
|
||||
final override Language::LanguageType getType() { result = vg.getType() }
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
if allocationEscapes(this.getAnAllocation())
|
||||
then result = TAllAliasedMemory(vg.getIRFunction(), false)
|
||||
else result = TGroupedMemoryLocation(vg, false, true)
|
||||
}
|
||||
|
||||
/** Gets an allocation of this memory location. */
|
||||
override Allocation getAnAllocation() { result = vg.getAnAllocation() }
|
||||
|
||||
/** Gets the set of allocations associated with this memory location. */
|
||||
VariableGroup getGroup() { result = vg }
|
||||
|
||||
/** Holds if this memory location represents all the enclosing allocations. */
|
||||
predicate isAll() { isAll = true }
|
||||
|
||||
/** Holds if this memory location represents one or more of the enclosing allocations. */
|
||||
predicate isSome() { isAll = false }
|
||||
}
|
||||
|
||||
private GroupedMemoryLocation getGroupedMemoryLocation(
|
||||
Allocation alloc, boolean isMayAccess, boolean isAll
|
||||
) {
|
||||
result.getAnAllocation() = alloc and
|
||||
(
|
||||
isMayAccess = true and result.isMayAccess()
|
||||
or
|
||||
isMayAccess = false and not result.isMayAccess()
|
||||
) and
|
||||
(
|
||||
isAll = true and result.isAll()
|
||||
or
|
||||
isAll = false and result.isSome()
|
||||
)
|
||||
}
|
||||
|
||||
class EntireAllocationMemoryLocation extends TEntireAllocationMemoryLocation,
|
||||
AllocationMemoryLocation
|
||||
{
|
||||
@@ -282,10 +519,18 @@ class VariableVirtualVariable extends VariableMemoryLocation, VirtualVariable {
|
||||
}
|
||||
}
|
||||
|
||||
class GroupedVirtualVariable extends GroupedMemoryLocation, VirtualVariable {
|
||||
GroupedVirtualVariable() {
|
||||
forex(Allocation var | var = this.getAnAllocation() | not allocationEscapes(var)) and
|
||||
not this.isMayAccess() and
|
||||
this.isAll()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to memory that is not known to be confined to a specific `IRVariable`.
|
||||
*/
|
||||
class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
|
||||
class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation0 {
|
||||
IRFunction irFunc;
|
||||
boolean isMayAccess;
|
||||
|
||||
@@ -312,7 +557,7 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
|
||||
* An access to memory that is not known to be confined to a specific `IRVariable`, but is known to
|
||||
* not access memory on the current function's stack frame.
|
||||
*/
|
||||
class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
|
||||
class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation0 {
|
||||
IRFunction irFunc;
|
||||
boolean isMayAccess;
|
||||
|
||||
@@ -346,7 +591,7 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
|
||||
/**
|
||||
* An access to all aliased memory.
|
||||
*/
|
||||
class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
|
||||
class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation0 {
|
||||
IRFunction irFunc;
|
||||
boolean isMayAccess;
|
||||
|
||||
@@ -377,7 +622,7 @@ class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
|
||||
/**
|
||||
* Gets the overlap relationship between the definition location `def` and the use location `use`.
|
||||
*/
|
||||
Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
Overlap getOverlap(MemoryLocation0 def, MemoryLocation0 use) {
|
||||
exists(Overlap overlap |
|
||||
// Compute the overlap based only on the extent.
|
||||
overlap = getExtentOverlap(def, use) and
|
||||
@@ -405,7 +650,7 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
* based only on the set of memory locations accessed. Handling of "may" accesses and read-only
|
||||
* locations occurs in `getOverlap()`.
|
||||
*/
|
||||
private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
private Overlap getExtentOverlap(MemoryLocation0 def, MemoryLocation0 use) {
|
||||
// The def and the use must have the same virtual variable, or no overlap is possible.
|
||||
(
|
||||
// AllAliasedMemory must totally overlap any location within the same virtual variable.
|
||||
@@ -446,7 +691,7 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
result instanceof MustExactlyOverlap
|
||||
or
|
||||
not use instanceof EntireAllocationMemoryLocation and
|
||||
if def.getAllocation() = use.getAllocation()
|
||||
if def.getAnAllocation() = use.getAnAllocation()
|
||||
then
|
||||
// EntireAllocationMemoryLocation totally overlaps any location within
|
||||
// the same allocation.
|
||||
@@ -454,11 +699,48 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
else (
|
||||
// There is no overlap with a location that's known to belong to a
|
||||
// different allocation, but all other locations may partially overlap.
|
||||
not exists(use.getAllocation()) and
|
||||
not exists(use.getAnAllocation()) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(GroupedMemoryLocation group |
|
||||
group = def and
|
||||
def.getVirtualVariable() = use.getVirtualVariable()
|
||||
|
|
||||
(
|
||||
use instanceof UnknownMemoryLocation or
|
||||
use instanceof AllAliasedMemory
|
||||
) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
group.isAll() and
|
||||
(
|
||||
group.getAnAllocation() =
|
||||
[
|
||||
use.(EntireAllocationMemoryLocation).getAnAllocation(),
|
||||
use.(VariableMemoryLocation).getAnAllocation()
|
||||
]
|
||||
or
|
||||
use.(GroupedMemoryLocation).isSome()
|
||||
) and
|
||||
result instanceof MustTotallyOverlap
|
||||
or
|
||||
group.isAll() and
|
||||
use.(GroupedMemoryLocation).isAll() and
|
||||
result instanceof MustExactlyOverlap
|
||||
or
|
||||
group.isSome() and
|
||||
(
|
||||
use instanceof EntireAllocationMemoryLocation
|
||||
or
|
||||
use instanceof VariableMemoryLocation
|
||||
or
|
||||
use instanceof GroupedMemoryLocation
|
||||
) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
)
|
||||
or
|
||||
exists(VariableMemoryLocation defVariableLocation |
|
||||
defVariableLocation = def and
|
||||
(
|
||||
@@ -468,7 +750,8 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
(
|
||||
use instanceof UnknownMemoryLocation or
|
||||
use instanceof AllAliasedMemory or
|
||||
use instanceof EntireAllocationMemoryLocation
|
||||
use instanceof EntireAllocationMemoryLocation or
|
||||
use instanceof GroupedMemoryLocation
|
||||
) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
@@ -534,7 +817,7 @@ private predicate isCoveredOffset(Allocation var, int offsetRank, VariableMemory
|
||||
exists(int startRank, int endRank, VirtualVariable vvar |
|
||||
vml.getStartBitOffset() = rank[startRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and
|
||||
vml.getEndBitOffset() = rank[endRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and
|
||||
var = vml.getAllocation() and
|
||||
var = vml.getAnAllocation() and
|
||||
vvar = vml.getVirtualVariable() and
|
||||
isRelatableMemoryLocation(vml) and
|
||||
offsetRank in [startRank .. endRank]
|
||||
@@ -542,7 +825,7 @@ private predicate isCoveredOffset(Allocation var, int offsetRank, VariableMemory
|
||||
}
|
||||
|
||||
private predicate hasUnknownOffset(Allocation var, VariableMemoryLocation vml) {
|
||||
vml.getAllocation() = var and
|
||||
vml.getAnAllocation() = var and
|
||||
(
|
||||
vml.getStartBitOffset() = Ints::unknown() or
|
||||
vml.getEndBitOffset() = Ints::unknown()
|
||||
@@ -557,9 +840,9 @@ private predicate overlappingIRVariableMemoryLocations(
|
||||
isCoveredOffset(var, offsetRank, use)
|
||||
)
|
||||
or
|
||||
hasUnknownOffset(use.getAllocation(), def)
|
||||
hasUnknownOffset(use.getAnAllocation(), def)
|
||||
or
|
||||
hasUnknownOffset(def.getAllocation(), use)
|
||||
hasUnknownOffset(def.getAnAllocation(), use)
|
||||
}
|
||||
|
||||
private Overlap getVariableMemoryLocationOverlap(
|
||||
@@ -580,6 +863,40 @@ predicate canReuseSsaForOldResult(Instruction instr) { OldSsa::canReuseSsaForMem
|
||||
bindingset[result, b]
|
||||
private boolean unbindBool(boolean b) { result != b.booleanNot() }
|
||||
|
||||
/** Gets the number of overlapping uses of `def`. */
|
||||
private int numberOfOverlappingUses(MemoryLocation0 def) {
|
||||
result = strictcount(MemoryLocation0 use | exists(getOverlap(def, use)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is a busy definition. That is, it has a large number of
|
||||
* overlapping uses.
|
||||
*/
|
||||
private predicate isBusyDef(MemoryLocation0 def) { numberOfOverlappingUses(def) > 1024 }
|
||||
|
||||
/** Holds if `use` is a use that overlaps with a busy definition. */
|
||||
private predicate useOverlapWithBusyDef(MemoryLocation0 use) {
|
||||
exists(MemoryLocation0 def |
|
||||
exists(getOverlap(def, use)) and
|
||||
isBusyDef(def)
|
||||
)
|
||||
}
|
||||
|
||||
final private class FinalMemoryLocation = MemoryLocation0;
|
||||
|
||||
/**
|
||||
* A memory location accessed by a memory operand or memory result. In this implementation, the location is
|
||||
* one of the following:
|
||||
* - `VariableMemoryLocation` - A location within a known `IRVariable`, at an offset that is either a constant or is
|
||||
* unknown.
|
||||
* - `UnknownMemoryLocation` - A location not known to be within a specific `IRVariable`.
|
||||
*
|
||||
* Compared to `MemoryLocation0`, this class does not contain memory locations that represent uses of busy definitions.
|
||||
*/
|
||||
class MemoryLocation extends FinalMemoryLocation {
|
||||
MemoryLocation() { not useOverlapWithBusyDef(this) }
|
||||
}
|
||||
|
||||
MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
not canReuseSsaForOldResult(instr) and
|
||||
exists(MemoryAccessKind kind, boolean isMayAccess |
|
||||
@@ -588,13 +905,24 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
(
|
||||
(
|
||||
isIndirectOrBufferMemoryAccess(kind) and
|
||||
if hasResultMemoryAccess(instr, _, _, _, _, _, _)
|
||||
if hasResultMemoryAccess(_, instr, _, _, _, _, _, _, _)
|
||||
then
|
||||
exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset, isMayAccess) and
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset,
|
||||
unbindBool(isMayAccess))
|
||||
exists(
|
||||
Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset,
|
||||
boolean grouped
|
||||
|
|
||||
hasResultMemoryAccess(_, instr, var, type, _, startBitOffset, endBitOffset, isMayAccess,
|
||||
grouped)
|
||||
|
|
||||
// If the instruction is only associated with one allocation we assign it a `VariableMemoryLocation`
|
||||
if grouped = false
|
||||
then
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset,
|
||||
unbindBool(isMayAccess))
|
||||
else
|
||||
// And otherwise we assign it a memory location that groups all the relevant memory locations into one.
|
||||
result = getGroupedMemoryLocation(var, unbindBool(isMayAccess), false)
|
||||
)
|
||||
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
@@ -613,20 +941,31 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
)
|
||||
}
|
||||
|
||||
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
private MemoryLocation0 getOperandMemoryLocation0(MemoryOperand operand, boolean isMayAccess) {
|
||||
not canReuseSsaForOldResult(operand.getAnyDef()) and
|
||||
exists(MemoryAccessKind kind, boolean isMayAccess |
|
||||
exists(MemoryAccessKind kind |
|
||||
kind = operand.getMemoryAccess() and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
(
|
||||
(
|
||||
isIndirectOrBufferMemoryAccess(kind) and
|
||||
if hasOperandMemoryAccess(operand, _, _, _, _, _, _)
|
||||
if hasOperandMemoryAccess(_, operand, _, _, _, _, _, _, _)
|
||||
then
|
||||
exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, _, startBitOffset, endBitOffset, isMayAccess) and
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
exists(
|
||||
Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset,
|
||||
boolean grouped
|
||||
|
|
||||
hasOperandMemoryAccess(_, operand, var, type, _, startBitOffset, endBitOffset,
|
||||
isMayAccess, grouped)
|
||||
|
|
||||
// If the operand is only associated with one memory location we assign it a `VariableMemoryLocation`
|
||||
if grouped = false
|
||||
then
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
else
|
||||
// And otherwise we assign it a memory location that groups all relevant memory locations into one.
|
||||
result = getGroupedMemoryLocation(var, isMayAccess, false)
|
||||
)
|
||||
else result = TUnknownMemoryLocation(operand.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
@@ -645,6 +984,19 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
)
|
||||
}
|
||||
|
||||
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
exists(MemoryLocation0 use0, boolean isMayAccess |
|
||||
use0 = getOperandMemoryLocation0(operand, isMayAccess)
|
||||
|
|
||||
result = use0
|
||||
or
|
||||
// If `use0` overlaps with a busy definition we turn it into a use
|
||||
// of `UnknownMemoryLocation`.
|
||||
not use0 instanceof MemoryLocation and
|
||||
result = TUnknownMemoryLocation(operand.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the start bit offset of a `MemoryLocation`, if any. */
|
||||
int getStartBitOffset(VariableMemoryLocation location) {
|
||||
result = location.getStartBitOffset() and Ints::hasValue(result)
|
||||
|
||||
@@ -15,6 +15,51 @@ private class OldInstruction = Reachability::ReachableInstruction;
|
||||
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Holds if `instruction` is the first instruction that may be followed by
|
||||
* an `UninitializedGroup` instruction, and the enclosing function of
|
||||
* `instruction` is `func`.
|
||||
*/
|
||||
private predicate isFirstInstructionBeforeUninitializedGroup(
|
||||
Instruction instruction, IRFunction func
|
||||
) {
|
||||
instruction = getChi(any(OldIR::InitializeNonLocalInstruction init)) and
|
||||
func = instruction.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/** Gets the `i`'th `UninitializedGroup` instruction in `func`. */
|
||||
private UninitializedGroupInstruction getInitGroupInstruction(int i, IRFunction func) {
|
||||
exists(Alias::VariableGroup vg |
|
||||
vg.getIRFunction() = func and
|
||||
vg.getInitializationOrder() = i and
|
||||
result = uninitializedGroup(vg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instruction` is the last instruction in the chain of `UninitializedGroup`
|
||||
* instructions in `func`. The chain of instructions may be empty in which case
|
||||
* `instruction` satisfies
|
||||
* ```
|
||||
* isFirstInstructionBeforeUninitializedGroup(instruction, func)
|
||||
* ```
|
||||
*/
|
||||
predicate isLastInstructionForUninitializedGroups(Instruction instruction, IRFunction func) {
|
||||
exists(int i |
|
||||
instruction = getInitGroupInstruction(i, func) and
|
||||
not exists(getChi(instruction)) and
|
||||
not exists(getInitGroupInstruction(i + 1, func))
|
||||
)
|
||||
or
|
||||
exists(int i |
|
||||
instruction = getChi(getInitGroupInstruction(i, func)) and
|
||||
not exists(getInitGroupInstruction(i + 1, func))
|
||||
)
|
||||
or
|
||||
isFirstInstructionBeforeUninitializedGroup(instruction, func) and
|
||||
not exists(getInitGroupInstruction(0, func))
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
@@ -32,6 +77,11 @@ private module Cached {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiNodeAfterUninitializedGroup(UninitializedGroupInstruction initGroup) {
|
||||
hasChiNodeAfterUninitializedGroup(_, initGroup)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
@@ -45,7 +95,8 @@ private module Cached {
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction or
|
||||
TUninitializedGroupInstruction;
|
||||
|
||||
/**
|
||||
* If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block,
|
||||
@@ -78,6 +129,8 @@ private module Cached {
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUninitializedGroupInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
@@ -123,7 +176,8 @@ private module Cached {
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
canModelResultForOldInstruction(getOldInstruction(instruction)) or
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
instruction instanceof ChiInstruction or // Chis always have modeled results
|
||||
instruction instanceof UninitializedGroupInstruction // Group initializers always have modeled results
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -134,16 +188,23 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
exists(Instruction input | instruction = getChi(input) |
|
||||
Alias::getResultMemoryLocation(input).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
or
|
||||
// A chi following an `UninitializedGroupInstruction` only happens when the virtual
|
||||
// variable of the grouped memory location is `{AllAliasedMemory}`.
|
||||
exists(Alias::GroupedMemoryLocation gml |
|
||||
input = uninitializedGroup(gml.getGroup()) and
|
||||
gml.getVirtualVariable() instanceof Alias::AliasedVirtualVariable
|
||||
)
|
||||
)
|
||||
or
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
not exists(location.getAnAllocation())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -205,7 +266,11 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
(
|
||||
instruction = getChi(getOldInstruction(result))
|
||||
or
|
||||
instruction = getChi(result.(UninitializedGroupInstruction))
|
||||
) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -263,6 +328,14 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getAnUninitializedGroupVariable(UninitializedGroupInstruction init) {
|
||||
exists(Alias::VariableGroup vg |
|
||||
init = uninitializedGroup(vg) and
|
||||
result = vg.getAnAllocation().getABaseInstruction().(VariableInstruction).getIRVariable()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
@@ -316,6 +389,19 @@ private module Cached {
|
||||
result = getNewPhiOperandDefinitionFromOldSsa(instr, newPredecessorBlock, overlap)
|
||||
}
|
||||
|
||||
private ChiInstruction getChiAfterUninitializedGroup(int i, IRFunction func) {
|
||||
result =
|
||||
rank[i + 1](VariableGroup vg, UninitializedGroupInstruction initGroup, ChiInstruction chi,
|
||||
int r |
|
||||
initGroup.getEnclosingIRFunction() = func and
|
||||
chi = getChi(initGroup) and
|
||||
initGroup = uninitializedGroup(vg) and
|
||||
r = vg.getInitializationOrder()
|
||||
|
|
||||
chi order by r
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(
|
||||
@@ -329,6 +415,19 @@ private module Cached {
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar, _)
|
||||
)
|
||||
or
|
||||
exists(UninitializedGroupInstruction initGroup, IRFunction func |
|
||||
chiInstr = getChi(initGroup) and
|
||||
func = initGroup.getEnclosingIRFunction()
|
||||
|
|
||||
chiInstr = getChiAfterUninitializedGroup(0, func) and
|
||||
isFirstInstructionBeforeUninitializedGroup(result, func)
|
||||
or
|
||||
exists(int i |
|
||||
chiInstr = getChiAfterUninitializedGroup(i + 1, func) and
|
||||
result = getChiAfterUninitializedGroup(i, func)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -344,14 +443,40 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction
|
||||
*/
|
||||
private UninitializedGroupInstruction firstInstructionToUninitializedGroup(
|
||||
Instruction instruction, EdgeKind kind
|
||||
) {
|
||||
exists(IRFunction func |
|
||||
isFirstInstructionBeforeUninitializedGroup(instruction, func) and
|
||||
result = getInitGroupInstruction(0, func) and
|
||||
kind instanceof GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
private Instruction getNextUninitializedGroupInstruction(Instruction instruction, EdgeKind kind) {
|
||||
exists(int i, IRFunction func |
|
||||
func = instruction.getEnclosingIRFunction() and
|
||||
instruction = getInitGroupInstruction(i, func) and
|
||||
kind instanceof GotoEdge
|
||||
|
|
||||
if hasChiNodeAfterUninitializedGroup(_, instruction)
|
||||
then result = getChi(instruction)
|
||||
else result = getInitGroupInstruction(i + 1, func)
|
||||
)
|
||||
or
|
||||
exists(int i, IRFunction func, UninitializedGroupInstruction initGroup |
|
||||
func = instruction.getEnclosingIRFunction() and
|
||||
instruction = getChi(initGroup) and
|
||||
initGroup = getInitGroupInstruction(i, func) and
|
||||
kind instanceof GotoEdge
|
||||
|
|
||||
result = getInitGroupInstruction(i + 1, func)
|
||||
)
|
||||
}
|
||||
|
||||
private Instruction getInstructionSuccessorAfterUninitializedGroup0(
|
||||
Instruction instruction, EdgeKind kind
|
||||
) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
@@ -371,6 +496,107 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private Instruction getInstructionSuccessorAfterUninitializedGroup(
|
||||
Instruction instruction, EdgeKind kind
|
||||
) {
|
||||
exists(IRFunction func, Instruction firstBeforeUninitializedGroup |
|
||||
isLastInstructionForUninitializedGroups(instruction, func) and
|
||||
isFirstInstructionBeforeUninitializedGroup(firstBeforeUninitializedGroup, func) and
|
||||
result = getInstructionSuccessorAfterUninitializedGroup0(firstBeforeUninitializedGroup, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction.
|
||||
*
|
||||
* Furthermore, the entry block is augmented with `UninitializedGroup` instructions and `Chi`
|
||||
* instructions. For example, consider this example:
|
||||
* ```cpp
|
||||
* int x, y;
|
||||
* int* p;
|
||||
* if(b) {
|
||||
* p = &x;
|
||||
* escape(&x);
|
||||
* } else {
|
||||
* p = &y;
|
||||
* }
|
||||
* *p = 42;
|
||||
*
|
||||
* int z, w;
|
||||
* int* q;
|
||||
* if(b) {
|
||||
* q = &z;
|
||||
* } else {
|
||||
* q = &w;
|
||||
* }
|
||||
* *q = 43;
|
||||
* ```
|
||||
*
|
||||
* the unaliased IR for the entry block of this snippet is:
|
||||
* ```
|
||||
* v1(void) = EnterFunction :
|
||||
* m1(unknown) = AliasedDefinition :
|
||||
* m2(unknown) = InitializeNonLocal :
|
||||
* r1(glval<bool>) = VariableAddress[b] :
|
||||
* m3(bool) = InitializeParameter[b] : &:r1
|
||||
* r2(glval<int>) = VariableAddress[x] :
|
||||
* m4(int) = Uninitialized[x] : &:r2
|
||||
* r3(glval<int>) = VariableAddress[y] :
|
||||
* m5(int) = Uninitialized[y] : &:r3
|
||||
* r4(glval<int *>) = VariableAddress[p] :
|
||||
* m6(int *) = Uninitialized[p] : &:r4
|
||||
* r5(glval<bool>) = VariableAddress[b] :
|
||||
* r6(bool) = Load[b] : &:r5, m3
|
||||
* v2(void) = ConditionalBranch : r6
|
||||
* ```
|
||||
* and we need to transform this to aliased IR by inserting an `UninitializedGroup`
|
||||
* instruction for every `VariableGroup` memory location in the function. Furthermore,
|
||||
* if the `VariableGroup` memory location contains an allocation that escapes we need
|
||||
* to insert a `Chi` that writes the memory produced by `UninitializedGroup` into
|
||||
* `{AllAliasedMemory}`. For the above snippet we then end up with:
|
||||
* ```
|
||||
* v1(void) = EnterFunction :
|
||||
* m2(unknown) = AliasedDefinition :
|
||||
* m3(unknown) = InitializeNonLocal :
|
||||
* m4(unknown) = Chi : total:m2, partial:m3
|
||||
* m5(int) = UninitializedGroup[x,y] :
|
||||
* m6(unknown) = Chi : total:m4, partial:m5
|
||||
* m7(int) = UninitializedGroup[w,z] :
|
||||
* r1(glval<bool>) = VariableAddress[b] :
|
||||
* m8(bool) = InitializeParameter[b] : &:r1
|
||||
* r2(glval<int>) = VariableAddress[x] :
|
||||
* m10(int) = Uninitialized[x] : &:r2
|
||||
* m11(unknown) = Chi : total:m6, partial:m10
|
||||
* r3(glval<int>) = VariableAddress[y] :
|
||||
* m12(int) = Uninitialized[y] : &:r3
|
||||
* m13(unknown) = Chi : total:m11, partial:m12
|
||||
* r4(glval<int *>) = VariableAddress[p] :
|
||||
* m14(int *) = Uninitialized[p] : &:r4
|
||||
* r5(glval<bool>) = VariableAddress[b] :
|
||||
* r6(bool) = Load[b] : &:r5, m8
|
||||
* v2(void) = ConditionalBranch : r6
|
||||
* ```
|
||||
*
|
||||
* Here, the group `{x, y}` contains an allocation that escapes (`x`), so there
|
||||
* is a `Chi` after the `UninitializedGroup` that initializes the memory for the
|
||||
* `VariableGroup` containing `x`. None of the allocations in `{w, z}` escape so
|
||||
* there is no `Chi` following that the `UninitializedGroup` that initializes the
|
||||
* memory of `{w, z}`.
|
||||
*/
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result = firstInstructionToUninitializedGroup(instruction, kind)
|
||||
or
|
||||
result = getNextUninitializedGroupInstruction(instruction, kind)
|
||||
or
|
||||
result = getInstructionSuccessorAfterUninitializedGroup(instruction, kind)
|
||||
or
|
||||
not isFirstInstructionBeforeUninitializedGroup(instruction, _) and
|
||||
result = getInstructionSuccessorAfterUninitializedGroup0(instruction, kind)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
@@ -406,6 +632,16 @@ private module Cached {
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
or
|
||||
exists(Alias::VariableGroup vg |
|
||||
instr = uninitializedGroup(vg) and
|
||||
result = vg.getIRFunction().getFunction()
|
||||
)
|
||||
or
|
||||
exists(UninitializedGroupInstruction initGroup |
|
||||
instr = chiInstruction(initGroup) and
|
||||
result = getInstructionAst(initGroup)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -418,9 +654,16 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
instr = chiInstruction(primaryInstr) and result = vvar.getType()
|
||||
|
|
||||
hasChiNode(vvar, primaryInstr)
|
||||
or
|
||||
hasChiNodeAfterUninitializedGroup(vvar, primaryInstr)
|
||||
)
|
||||
or
|
||||
exists(Alias::VariableGroup vg |
|
||||
instr = uninitializedGroup(vg) and
|
||||
result = vg.getType()
|
||||
)
|
||||
or
|
||||
instr = reusedPhiInstruction(_) and
|
||||
@@ -448,6 +691,8 @@ private module Cached {
|
||||
or
|
||||
instr = chiInstruction(_) and opcode instanceof Opcode::Chi
|
||||
or
|
||||
instr = uninitializedGroup(_) and opcode instanceof Opcode::UninitializedGroup
|
||||
or
|
||||
instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
@@ -460,10 +705,15 @@ private module Cached {
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction primaryInstr |
|
||||
exists(Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
exists(Alias::VariableGroup vg |
|
||||
instr = uninitializedGroup(vg) and
|
||||
result = vg.getIRFunction()
|
||||
)
|
||||
or
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
@@ -478,6 +728,8 @@ private module Cached {
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
or
|
||||
instruction = getChi(result.(UninitializedGroupInstruction))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +737,7 @@ private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
private ChiInstruction getChi(Instruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
@@ -506,6 +758,16 @@ private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasChiNodeAfterUninitializedGroup(
|
||||
Alias::AliasedVirtualVariable vvar, UninitializedGroupInstruction initGroup
|
||||
) {
|
||||
exists(Alias::GroupedMemoryLocation defLocation |
|
||||
initGroup = uninitializedGroup(defLocation.getGroup()) and
|
||||
defLocation.getVirtualVariable() = vvar and
|
||||
Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap
|
||||
)
|
||||
}
|
||||
|
||||
private import PhiInsertion
|
||||
|
||||
/**
|
||||
@@ -668,19 +930,37 @@ private import DefUse
|
||||
* potentially very sparse.
|
||||
*/
|
||||
module DefUse {
|
||||
bindingset[index, block]
|
||||
pragma[inline_late]
|
||||
private int getNonChiOffset(int index, OldBlock block) {
|
||||
exists(OldIR::IRFunction func, Instruction i, OldBlock entryBlock |
|
||||
func = block.getEnclosingIRFunction() and
|
||||
i = block.getInstruction(index) and
|
||||
entryBlock = func.getEntryBlock()
|
||||
|
|
||||
if
|
||||
block = entryBlock and
|
||||
not i instanceof InitializeNonLocalInstruction and
|
||||
not i instanceof AliasedDefinitionInstruction
|
||||
then result = 2 * (index + count(VariableGroup vg | vg.getIRFunction() = func))
|
||||
else result = 2 * index
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[index, block]
|
||||
pragma[inline_late]
|
||||
private int getChiOffset(int index, OldBlock block) { result = getNonChiOffset(index, block) + 1 }
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` for the definition at offset `defOffset` in block `defBlock`.
|
||||
*/
|
||||
Instruction getDefinitionOrChiInstruction(
|
||||
private Instruction getDefinitionOrChiInstruction0(
|
||||
OldBlock defBlock, int defOffset, Alias::MemoryLocation defLocation,
|
||||
Alias::MemoryLocation actualDefLocation
|
||||
) {
|
||||
exists(OldInstruction oldInstr, int oldOffset |
|
||||
oldInstr = defBlock.getInstruction(oldOffset) and
|
||||
oldOffset >= 0
|
||||
|
|
||||
exists(OldInstruction oldInstr, int oldOffset | oldInstr = defBlock.getInstruction(oldOffset) |
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
defOffset = getChiOffset(oldOffset, defBlock) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
@@ -689,7 +969,7 @@ module DefUse {
|
||||
actualDefLocation = defLocation.getVirtualVariable()
|
||||
or
|
||||
// An even offset corresponds to the original instruction.
|
||||
defOffset = oldOffset * 2 and
|
||||
defOffset = getNonChiOffset(oldOffset, defBlock) and
|
||||
result = getNewInstruction(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
@@ -702,6 +982,54 @@ module DefUse {
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
or
|
||||
exists(
|
||||
Alias::VariableGroup vg, int index, UninitializedGroupInstruction initGroup,
|
||||
Alias::GroupedMemoryLocation gml
|
||||
|
|
||||
// Add 3 to account for the function prologue:
|
||||
// v1(void) = EnterFunction
|
||||
// m1(unknown) = AliasedDefinition
|
||||
// m2(unknown) = InitializeNonLocal
|
||||
index = 3 + vg.getInitializationOrder() and
|
||||
not gml.isMayAccess() and
|
||||
gml.isSome() and
|
||||
gml.getGroup() = vg and
|
||||
vg.getIRFunction().getEntryBlock() = defBlock and
|
||||
initGroup = uninitializedGroup(vg) and
|
||||
(defLocation = gml or defLocation = gml.getVirtualVariable())
|
||||
|
|
||||
result = initGroup and
|
||||
defOffset = 2 * index and
|
||||
actualDefLocation = defLocation
|
||||
or
|
||||
result = getChi(initGroup) and
|
||||
defOffset = 2 * index + 1 and
|
||||
actualDefLocation = defLocation.getVirtualVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private ChiInstruction remapGetDefinitionOrChiInstruction(Instruction oldResult) {
|
||||
exists(IRFunction func |
|
||||
isFirstInstructionBeforeUninitializedGroup(oldResult, func) and
|
||||
isLastInstructionForUninitializedGroups(result, func)
|
||||
)
|
||||
}
|
||||
|
||||
Instruction getDefinitionOrChiInstruction(
|
||||
OldBlock defBlock, int defOffset, Alias::MemoryLocation defLocation,
|
||||
Alias::MemoryLocation actualDefLocation
|
||||
) {
|
||||
exists(Instruction oldResult |
|
||||
oldResult =
|
||||
getDefinitionOrChiInstruction0(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
(
|
||||
result = remapGetDefinitionOrChiInstruction(oldResult)
|
||||
or
|
||||
not exists(remapGetDefinitionOrChiInstruction(oldResult)) and
|
||||
result = oldResult
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -842,8 +1170,20 @@ module DefUse {
|
||||
block.getInstruction(index) = def and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation) and
|
||||
if overlap instanceof MayPartiallyOverlap
|
||||
then offset = (index * 2) + 1 // The use will be connected to the definition on the `Chi` instruction.
|
||||
else offset = index * 2 // The use will be connected to the definition on the original instruction.
|
||||
then offset = getChiOffset(index, block) // The use will be connected to the definition on the `Chi` instruction.
|
||||
else offset = getNonChiOffset(index, block) // The use will be connected to the definition on the original instruction.
|
||||
)
|
||||
or
|
||||
exists(UninitializedGroupInstruction initGroup, int index, Overlap overlap, VariableGroup vg |
|
||||
initGroup.getEnclosingIRFunction().getEntryBlock() = getNewBlock(block) and
|
||||
vg = defLocation.(Alias::GroupedMemoryLocation).getGroup() and
|
||||
// EnterFunction + AliasedDefinition + InitializeNonLocal + index
|
||||
index = 3 + vg.getInitializationOrder() and
|
||||
initGroup = uninitializedGroup(vg) and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation) and
|
||||
if overlap instanceof MayPartiallyOverlap and hasChiNodeAfterUninitializedGroup(initGroup)
|
||||
then offset = 2 * index + 1 // The use will be connected to the definition on the `Chi` instruction.
|
||||
else offset = 2 * index // The use will be connected to the definition on the original instruction.
|
||||
)
|
||||
}
|
||||
|
||||
@@ -904,10 +1244,11 @@ module DefUse {
|
||||
block.getInstruction(index) = use and
|
||||
(
|
||||
// A direct use of the location.
|
||||
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and offset = index * 2
|
||||
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and
|
||||
offset = getNonChiOffset(index, block)
|
||||
or
|
||||
// A `Chi` instruction will include a use of the virtual variable.
|
||||
hasChiNode(useLocation, use) and offset = (index * 2) + 1
|
||||
hasChiNode(useLocation, use) and offset = getChiOffset(index, block)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1057,5 +1398,9 @@ module Ssa {
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasChiNodeAfterUninitializedGroup = Cached::hasChiNodeAfterUninitializedGroup/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
|
||||
class VariableGroup = Alias::VariableGroup;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ newtype TInstruction =
|
||||
TUnaliasedSsaUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
UnaliasedSsa::Ssa::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TUnaliasedSsaUninitializedGroupInstruction(UnaliasedSsa::Ssa::VariableGroup vg) or
|
||||
TAliasedSsaPhiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSsa::Ssa::MemoryLocation memoryLocation
|
||||
) {
|
||||
@@ -41,6 +42,12 @@ newtype TInstruction =
|
||||
} or
|
||||
TAliasedSsaUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
AliasedSsa::Ssa::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TAliasedSsaUninitializedGroupInstruction(AliasedSsa::Ssa::VariableGroup vg) or
|
||||
TAliasedSsaChiAfterUninitializedGroupInstruction(
|
||||
TAliasedSsaUninitializedGroupInstruction initGroup
|
||||
) {
|
||||
AliasedSsa::Ssa::hasChiNodeAfterUninitializedGroup(initGroup)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +69,11 @@ module UnaliasedSsaInstructions {
|
||||
|
||||
class TChiInstruction = TUnaliasedSsaChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
class TUninitializedGroupInstruction = TUnaliasedSsaUninitializedGroupInstruction;
|
||||
|
||||
class TRawOrUninitializedGroupInstruction = TRawInstruction or TUninitializedGroupInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawOrUninitializedGroupInstruction primaryInstruction) {
|
||||
result = TUnaliasedSsaChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
@@ -71,6 +82,12 @@ module UnaliasedSsaInstructions {
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TUnaliasedSsaUnreachedInstruction(irFunc)
|
||||
}
|
||||
|
||||
class VariableGroup = UnaliasedSsa::Ssa::VariableGroup;
|
||||
|
||||
// This really should just be `TUnaliasedSsaUninitializedGroupInstruction`, but that makes the
|
||||
// compiler realize that certain expressions in `SSAConstruction` are unsatisfiable.
|
||||
TRawOrUninitializedGroupInstruction uninitializedGroup(VariableGroup vg) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,10 +109,16 @@ module AliasedSsaInstructions {
|
||||
result = TUnaliasedSsaPhiInstruction(blockStartInstr, _)
|
||||
}
|
||||
|
||||
class TChiInstruction = TAliasedSsaChiInstruction;
|
||||
class TChiInstruction =
|
||||
TAliasedSsaChiInstruction or TAliasedSsaChiAfterUninitializedGroupInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
class TRawOrInitialzieGroupInstruction =
|
||||
TRawInstruction or TAliasedSsaUninitializedGroupInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawOrInitialzieGroupInstruction primaryInstruction) {
|
||||
result = TAliasedSsaChiInstruction(primaryInstruction)
|
||||
or
|
||||
result = TAliasedSsaChiAfterUninitializedGroupInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TAliasedSsaUnreachedInstruction;
|
||||
@@ -103,4 +126,12 @@ module AliasedSsaInstructions {
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TAliasedSsaUnreachedInstruction(irFunc)
|
||||
}
|
||||
|
||||
class VariableGroup = AliasedSsa::Ssa::VariableGroup;
|
||||
|
||||
class TUninitializedGroupInstruction = TAliasedSsaUninitializedGroupInstruction;
|
||||
|
||||
TUninitializedGroupInstruction uninitializedGroup(VariableGroup vg) {
|
||||
result = TAliasedSsaUninitializedGroupInstruction(vg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ private import semmle.code.cpp.ir.internal.Overlap
|
||||
* Provides the newtype used to represent operands across all phases of the IR.
|
||||
*/
|
||||
private module Internal {
|
||||
private class TAliasedChiInstruction =
|
||||
TAliasedSsaChiInstruction or TAliasedSsaChiAfterUninitializedGroupInstruction;
|
||||
|
||||
/**
|
||||
* An IR operand. `TOperand` is shared across all phases of the IR. There are branches of this
|
||||
* type for operands created directly from the AST (`TRegisterOperand` and `TNonSSAMemoryOperand`),
|
||||
@@ -52,7 +55,7 @@ private module Internal {
|
||||
) {
|
||||
exists(AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap))
|
||||
} or
|
||||
TAliasedChiOperand(TAliasedSsaChiInstruction useInstr, ChiOperandTag tag) { any() }
|
||||
TAliasedChiOperand(TAliasedChiInstruction useInstr, ChiOperandTag tag) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,10 +201,13 @@ module AliasedSsaOperands {
|
||||
)
|
||||
}
|
||||
|
||||
private class TChiInstruction =
|
||||
TAliasedSsaChiInstruction or TAliasedSsaChiAfterUninitializedGroupInstruction;
|
||||
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
TChiOperand chiOperand(TAliasedSsaChiInstruction useInstr, ChiOperandTag tag) {
|
||||
TChiOperand chiOperand(TChiInstruction useInstr, ChiOperandTag tag) {
|
||||
result = Internal::TAliasedChiOperand(useInstr, tag)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2142,6 +2142,47 @@ class ChiInstruction extends Instruction {
|
||||
final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes a set of allocations that are each assigned
|
||||
* the same "virtual variable".
|
||||
*
|
||||
* As an example, consider the following snippet:
|
||||
* ```
|
||||
* int a;
|
||||
* int b;
|
||||
* int* p;
|
||||
* if(b) {
|
||||
* p = &a;
|
||||
* } else {
|
||||
* p = &b;
|
||||
* }
|
||||
* *p = 5;
|
||||
* int x = a;
|
||||
* ```
|
||||
*
|
||||
* Since both the address of `a` and `b` reach `p` at `*p = 5` the IR alias
|
||||
* analysis will create a region that contains both `a` and `b`. The region
|
||||
* containing both `a` and `b` are initialized by an `UninitializedGroup`
|
||||
* instruction in the entry block of the enclosing function.
|
||||
*/
|
||||
class UninitializedGroupInstruction extends Instruction {
|
||||
UninitializedGroupInstruction() { this.getOpcode() instanceof Opcode::UninitializedGroup }
|
||||
|
||||
/**
|
||||
* Gets an `IRVariable` whose memory is initialized by this instruction, if any.
|
||||
* Note: Allocations that are not represented as `IRVariable`s (such as
|
||||
* dynamic allocations) are not returned by this predicate even if this
|
||||
* instruction initializes such memory.
|
||||
*/
|
||||
final IRVariable getAnIRVariable() {
|
||||
result = Construction::getAnUninitializedGroupVariable(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
result = strictconcat(this.getAnIRVariable().toString(), ",")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
|
||||
@@ -407,6 +407,8 @@ predicate hasUnreachedInstruction(IRFunction func) {
|
||||
)
|
||||
}
|
||||
|
||||
IRVariable getAnUninitializedGroupVariable(UninitializedGroupInstruction instr) { none() }
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
cached
|
||||
|
||||
@@ -2142,6 +2142,47 @@ class ChiInstruction extends Instruction {
|
||||
final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes a set of allocations that are each assigned
|
||||
* the same "virtual variable".
|
||||
*
|
||||
* As an example, consider the following snippet:
|
||||
* ```
|
||||
* int a;
|
||||
* int b;
|
||||
* int* p;
|
||||
* if(b) {
|
||||
* p = &a;
|
||||
* } else {
|
||||
* p = &b;
|
||||
* }
|
||||
* *p = 5;
|
||||
* int x = a;
|
||||
* ```
|
||||
*
|
||||
* Since both the address of `a` and `b` reach `p` at `*p = 5` the IR alias
|
||||
* analysis will create a region that contains both `a` and `b`. The region
|
||||
* containing both `a` and `b` are initialized by an `UninitializedGroup`
|
||||
* instruction in the entry block of the enclosing function.
|
||||
*/
|
||||
class UninitializedGroupInstruction extends Instruction {
|
||||
UninitializedGroupInstruction() { this.getOpcode() instanceof Opcode::UninitializedGroup }
|
||||
|
||||
/**
|
||||
* Gets an `IRVariable` whose memory is initialized by this instruction, if any.
|
||||
* Note: Allocations that are not represented as `IRVariable`s (such as
|
||||
* dynamic allocations) are not returned by this predicate even if this
|
||||
* instruction initializes such memory.
|
||||
*/
|
||||
final IRVariable getAnIRVariable() {
|
||||
result = Construction::getAnUninitializedGroupVariable(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
result = strictconcat(this.getAnIRVariable().toString(), ",")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
|
||||
@@ -106,8 +106,7 @@ private predicate operandEscapesDomain(Operand operand) {
|
||||
not isArgumentForParameter(_, operand, _) and
|
||||
not isOnlyEscapesViaReturnArgument(operand) and
|
||||
not operand.getUse() instanceof ReturnValueInstruction and
|
||||
not operand.getUse() instanceof ReturnIndirectionInstruction and
|
||||
not operand instanceof PhiInputOperand
|
||||
not operand.getUse() instanceof ReturnIndirectionInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,6 +190,11 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instr
|
||||
// A copy propagates the source value.
|
||||
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
|
||||
)
|
||||
or
|
||||
operand = instr.(PhiInstruction).getAnInputOperand() and
|
||||
// Using `unknown` ensures termination since we cannot keep incrementing a bit offset
|
||||
// through the back edge of a loop (or through recursion).
|
||||
bitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
private predicate operandEscapesNonReturn(Operand operand) {
|
||||
@@ -212,9 +216,6 @@ private predicate operandEscapesNonReturn(Operand operand) {
|
||||
or
|
||||
isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUse())
|
||||
or
|
||||
operand instanceof PhiInputOperand and
|
||||
resultEscapesNonReturn(operand.getUse())
|
||||
or
|
||||
operandEscapesDomain(operand)
|
||||
}
|
||||
|
||||
@@ -236,9 +237,6 @@ private predicate operandMayReachReturn(Operand operand) {
|
||||
operand.getUse() instanceof ReturnValueInstruction
|
||||
or
|
||||
isOnlyEscapesViaReturnArgument(operand) and resultMayReachReturn(operand.getUse())
|
||||
or
|
||||
operand instanceof PhiInputOperand and
|
||||
resultMayReachReturn(operand.getUse())
|
||||
}
|
||||
|
||||
private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||
@@ -340,6 +338,56 @@ private predicate resultEscapesNonReturn(Instruction instr) {
|
||||
not instr.isResultModeled()
|
||||
}
|
||||
|
||||
/** Holds if `operand` may (transitively) flow to an `AddressOperand`. */
|
||||
private predicate consumedAsAddressOperand(Operand operand) {
|
||||
operand instanceof AddressOperand
|
||||
or
|
||||
exists(Operand address |
|
||||
consumedAsAddressOperand(address) and
|
||||
operandIsPropagated(operand, _, address.getDef())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operand` may originate from a base instruction of an allocation,
|
||||
* and that operand may transitively flow to an `AddressOperand`.
|
||||
*/
|
||||
private predicate propagatedFromAllocationBase(Operand operand, Configuration::Allocation allocation) {
|
||||
consumedAsAddressOperand(operand) and
|
||||
(
|
||||
not exists(Configuration::getOldAllocation(allocation)) and
|
||||
operand.getDef() = allocation.getABaseInstruction()
|
||||
or
|
||||
exists(Operand address |
|
||||
operandIsPropagated(address, _, operand.getDef()) and
|
||||
propagatedFromAllocationBase(address, allocation)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate propagatedFromNonAllocationBase(Operand operand) {
|
||||
exists(Instruction def |
|
||||
def = operand.getDef() and
|
||||
not operandIsPropagated(_, _, def) and
|
||||
not def = any(Configuration::Allocation allocation).getABaseInstruction()
|
||||
)
|
||||
or
|
||||
exists(Operand address |
|
||||
operandIsPropagated(address, _, operand.getDef()) and
|
||||
propagatedFromNonAllocationBase(address)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we cannot see all producers of an operand for which allocation also flows into.
|
||||
*/
|
||||
private predicate operandConsumesEscaped(Configuration::Allocation allocation) {
|
||||
exists(AddressOperand address |
|
||||
propagatedFromAllocationBase(address, allocation) and
|
||||
propagatedFromNonAllocationBase(address)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the address of `allocation` escapes outside the domain of the analysis. This can occur
|
||||
* either because the allocation's address is taken within the function and escapes, or because the
|
||||
@@ -348,12 +396,14 @@ private predicate resultEscapesNonReturn(Instruction instr) {
|
||||
predicate allocationEscapes(Configuration::Allocation allocation) {
|
||||
allocation.alwaysEscapes()
|
||||
or
|
||||
exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() |
|
||||
resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
or
|
||||
operandConsumesEscaped(allocation)
|
||||
)
|
||||
or
|
||||
Configuration::phaseNeedsSoundEscapeAnalysis() and
|
||||
resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
(resultEscapesNonReturn(allocation.getABaseInstruction()) or operandConsumesEscaped(allocation))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import AliasConfigurationImports
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* A memory allocation that can be tracked by the SimpleSSA alias analysis.
|
||||
@@ -16,3 +17,5 @@ class Allocation extends IRAutomaticVariable {
|
||||
}
|
||||
|
||||
predicate phaseNeedsSoundEscapeAnalysis() { any() }
|
||||
|
||||
Unit getOldAllocation(Allocation allocation) { any() }
|
||||
|
||||
@@ -15,6 +15,51 @@ private class OldInstruction = Reachability::ReachableInstruction;
|
||||
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Holds if `instruction` is the first instruction that may be followed by
|
||||
* an `UninitializedGroup` instruction, and the enclosing function of
|
||||
* `instruction` is `func`.
|
||||
*/
|
||||
private predicate isFirstInstructionBeforeUninitializedGroup(
|
||||
Instruction instruction, IRFunction func
|
||||
) {
|
||||
instruction = getChi(any(OldIR::InitializeNonLocalInstruction init)) and
|
||||
func = instruction.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/** Gets the `i`'th `UninitializedGroup` instruction in `func`. */
|
||||
private UninitializedGroupInstruction getInitGroupInstruction(int i, IRFunction func) {
|
||||
exists(Alias::VariableGroup vg |
|
||||
vg.getIRFunction() = func and
|
||||
vg.getInitializationOrder() = i and
|
||||
result = uninitializedGroup(vg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instruction` is the last instruction in the chain of `UninitializedGroup`
|
||||
* instructions in `func`. The chain of instructions may be empty in which case
|
||||
* `instruction` satisfies
|
||||
* ```
|
||||
* isFirstInstructionBeforeUninitializedGroup(instruction, func)
|
||||
* ```
|
||||
*/
|
||||
predicate isLastInstructionForUninitializedGroups(Instruction instruction, IRFunction func) {
|
||||
exists(int i |
|
||||
instruction = getInitGroupInstruction(i, func) and
|
||||
not exists(getChi(instruction)) and
|
||||
not exists(getInitGroupInstruction(i + 1, func))
|
||||
)
|
||||
or
|
||||
exists(int i |
|
||||
instruction = getChi(getInitGroupInstruction(i, func)) and
|
||||
not exists(getInitGroupInstruction(i + 1, func))
|
||||
)
|
||||
or
|
||||
isFirstInstructionBeforeUninitializedGroup(instruction, func) and
|
||||
not exists(getInitGroupInstruction(0, func))
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
@@ -32,6 +77,11 @@ private module Cached {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiNodeAfterUninitializedGroup(UninitializedGroupInstruction initGroup) {
|
||||
hasChiNodeAfterUninitializedGroup(_, initGroup)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
@@ -45,7 +95,8 @@ private module Cached {
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction or
|
||||
TUninitializedGroupInstruction;
|
||||
|
||||
/**
|
||||
* If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block,
|
||||
@@ -78,6 +129,8 @@ private module Cached {
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUninitializedGroupInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
@@ -123,7 +176,8 @@ private module Cached {
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
canModelResultForOldInstruction(getOldInstruction(instruction)) or
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
instruction instanceof ChiInstruction or // Chis always have modeled results
|
||||
instruction instanceof UninitializedGroupInstruction // Group initializers always have modeled results
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -134,16 +188,23 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
exists(Instruction input | instruction = getChi(input) |
|
||||
Alias::getResultMemoryLocation(input).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
or
|
||||
// A chi following an `UninitializedGroupInstruction` only happens when the virtual
|
||||
// variable of the grouped memory location is `{AllAliasedMemory}`.
|
||||
exists(Alias::GroupedMemoryLocation gml |
|
||||
input = uninitializedGroup(gml.getGroup()) and
|
||||
gml.getVirtualVariable() instanceof Alias::AliasedVirtualVariable
|
||||
)
|
||||
)
|
||||
or
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
not exists(location.getAnAllocation())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -205,7 +266,11 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
(
|
||||
instruction = getChi(getOldInstruction(result))
|
||||
or
|
||||
instruction = getChi(result.(UninitializedGroupInstruction))
|
||||
) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -263,6 +328,14 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getAnUninitializedGroupVariable(UninitializedGroupInstruction init) {
|
||||
exists(Alias::VariableGroup vg |
|
||||
init = uninitializedGroup(vg) and
|
||||
result = vg.getAnAllocation().getABaseInstruction().(VariableInstruction).getIRVariable()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
@@ -316,6 +389,19 @@ private module Cached {
|
||||
result = getNewPhiOperandDefinitionFromOldSsa(instr, newPredecessorBlock, overlap)
|
||||
}
|
||||
|
||||
private ChiInstruction getChiAfterUninitializedGroup(int i, IRFunction func) {
|
||||
result =
|
||||
rank[i + 1](VariableGroup vg, UninitializedGroupInstruction initGroup, ChiInstruction chi,
|
||||
int r |
|
||||
initGroup.getEnclosingIRFunction() = func and
|
||||
chi = getChi(initGroup) and
|
||||
initGroup = uninitializedGroup(vg) and
|
||||
r = vg.getInitializationOrder()
|
||||
|
|
||||
chi order by r
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(
|
||||
@@ -329,6 +415,19 @@ private module Cached {
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar, _)
|
||||
)
|
||||
or
|
||||
exists(UninitializedGroupInstruction initGroup, IRFunction func |
|
||||
chiInstr = getChi(initGroup) and
|
||||
func = initGroup.getEnclosingIRFunction()
|
||||
|
|
||||
chiInstr = getChiAfterUninitializedGroup(0, func) and
|
||||
isFirstInstructionBeforeUninitializedGroup(result, func)
|
||||
or
|
||||
exists(int i |
|
||||
chiInstr = getChiAfterUninitializedGroup(i + 1, func) and
|
||||
result = getChiAfterUninitializedGroup(i, func)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -344,14 +443,40 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction
|
||||
*/
|
||||
private UninitializedGroupInstruction firstInstructionToUninitializedGroup(
|
||||
Instruction instruction, EdgeKind kind
|
||||
) {
|
||||
exists(IRFunction func |
|
||||
isFirstInstructionBeforeUninitializedGroup(instruction, func) and
|
||||
result = getInitGroupInstruction(0, func) and
|
||||
kind instanceof GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
private Instruction getNextUninitializedGroupInstruction(Instruction instruction, EdgeKind kind) {
|
||||
exists(int i, IRFunction func |
|
||||
func = instruction.getEnclosingIRFunction() and
|
||||
instruction = getInitGroupInstruction(i, func) and
|
||||
kind instanceof GotoEdge
|
||||
|
|
||||
if hasChiNodeAfterUninitializedGroup(_, instruction)
|
||||
then result = getChi(instruction)
|
||||
else result = getInitGroupInstruction(i + 1, func)
|
||||
)
|
||||
or
|
||||
exists(int i, IRFunction func, UninitializedGroupInstruction initGroup |
|
||||
func = instruction.getEnclosingIRFunction() and
|
||||
instruction = getChi(initGroup) and
|
||||
initGroup = getInitGroupInstruction(i, func) and
|
||||
kind instanceof GotoEdge
|
||||
|
|
||||
result = getInitGroupInstruction(i + 1, func)
|
||||
)
|
||||
}
|
||||
|
||||
private Instruction getInstructionSuccessorAfterUninitializedGroup0(
|
||||
Instruction instruction, EdgeKind kind
|
||||
) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
@@ -371,6 +496,107 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private Instruction getInstructionSuccessorAfterUninitializedGroup(
|
||||
Instruction instruction, EdgeKind kind
|
||||
) {
|
||||
exists(IRFunction func, Instruction firstBeforeUninitializedGroup |
|
||||
isLastInstructionForUninitializedGroups(instruction, func) and
|
||||
isFirstInstructionBeforeUninitializedGroup(firstBeforeUninitializedGroup, func) and
|
||||
result = getInstructionSuccessorAfterUninitializedGroup0(firstBeforeUninitializedGroup, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction.
|
||||
*
|
||||
* Furthermore, the entry block is augmented with `UninitializedGroup` instructions and `Chi`
|
||||
* instructions. For example, consider this example:
|
||||
* ```cpp
|
||||
* int x, y;
|
||||
* int* p;
|
||||
* if(b) {
|
||||
* p = &x;
|
||||
* escape(&x);
|
||||
* } else {
|
||||
* p = &y;
|
||||
* }
|
||||
* *p = 42;
|
||||
*
|
||||
* int z, w;
|
||||
* int* q;
|
||||
* if(b) {
|
||||
* q = &z;
|
||||
* } else {
|
||||
* q = &w;
|
||||
* }
|
||||
* *q = 43;
|
||||
* ```
|
||||
*
|
||||
* the unaliased IR for the entry block of this snippet is:
|
||||
* ```
|
||||
* v1(void) = EnterFunction :
|
||||
* m1(unknown) = AliasedDefinition :
|
||||
* m2(unknown) = InitializeNonLocal :
|
||||
* r1(glval<bool>) = VariableAddress[b] :
|
||||
* m3(bool) = InitializeParameter[b] : &:r1
|
||||
* r2(glval<int>) = VariableAddress[x] :
|
||||
* m4(int) = Uninitialized[x] : &:r2
|
||||
* r3(glval<int>) = VariableAddress[y] :
|
||||
* m5(int) = Uninitialized[y] : &:r3
|
||||
* r4(glval<int *>) = VariableAddress[p] :
|
||||
* m6(int *) = Uninitialized[p] : &:r4
|
||||
* r5(glval<bool>) = VariableAddress[b] :
|
||||
* r6(bool) = Load[b] : &:r5, m3
|
||||
* v2(void) = ConditionalBranch : r6
|
||||
* ```
|
||||
* and we need to transform this to aliased IR by inserting an `UninitializedGroup`
|
||||
* instruction for every `VariableGroup` memory location in the function. Furthermore,
|
||||
* if the `VariableGroup` memory location contains an allocation that escapes we need
|
||||
* to insert a `Chi` that writes the memory produced by `UninitializedGroup` into
|
||||
* `{AllAliasedMemory}`. For the above snippet we then end up with:
|
||||
* ```
|
||||
* v1(void) = EnterFunction :
|
||||
* m2(unknown) = AliasedDefinition :
|
||||
* m3(unknown) = InitializeNonLocal :
|
||||
* m4(unknown) = Chi : total:m2, partial:m3
|
||||
* m5(int) = UninitializedGroup[x,y] :
|
||||
* m6(unknown) = Chi : total:m4, partial:m5
|
||||
* m7(int) = UninitializedGroup[w,z] :
|
||||
* r1(glval<bool>) = VariableAddress[b] :
|
||||
* m8(bool) = InitializeParameter[b] : &:r1
|
||||
* r2(glval<int>) = VariableAddress[x] :
|
||||
* m10(int) = Uninitialized[x] : &:r2
|
||||
* m11(unknown) = Chi : total:m6, partial:m10
|
||||
* r3(glval<int>) = VariableAddress[y] :
|
||||
* m12(int) = Uninitialized[y] : &:r3
|
||||
* m13(unknown) = Chi : total:m11, partial:m12
|
||||
* r4(glval<int *>) = VariableAddress[p] :
|
||||
* m14(int *) = Uninitialized[p] : &:r4
|
||||
* r5(glval<bool>) = VariableAddress[b] :
|
||||
* r6(bool) = Load[b] : &:r5, m8
|
||||
* v2(void) = ConditionalBranch : r6
|
||||
* ```
|
||||
*
|
||||
* Here, the group `{x, y}` contains an allocation that escapes (`x`), so there
|
||||
* is a `Chi` after the `UninitializedGroup` that initializes the memory for the
|
||||
* `VariableGroup` containing `x`. None of the allocations in `{w, z}` escape so
|
||||
* there is no `Chi` following that the `UninitializedGroup` that initializes the
|
||||
* memory of `{w, z}`.
|
||||
*/
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result = firstInstructionToUninitializedGroup(instruction, kind)
|
||||
or
|
||||
result = getNextUninitializedGroupInstruction(instruction, kind)
|
||||
or
|
||||
result = getInstructionSuccessorAfterUninitializedGroup(instruction, kind)
|
||||
or
|
||||
not isFirstInstructionBeforeUninitializedGroup(instruction, _) and
|
||||
result = getInstructionSuccessorAfterUninitializedGroup0(instruction, kind)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
@@ -406,6 +632,16 @@ private module Cached {
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
or
|
||||
exists(Alias::VariableGroup vg |
|
||||
instr = uninitializedGroup(vg) and
|
||||
result = vg.getIRFunction().getFunction()
|
||||
)
|
||||
or
|
||||
exists(UninitializedGroupInstruction initGroup |
|
||||
instr = chiInstruction(initGroup) and
|
||||
result = getInstructionAst(initGroup)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -418,9 +654,16 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
instr = chiInstruction(primaryInstr) and result = vvar.getType()
|
||||
|
|
||||
hasChiNode(vvar, primaryInstr)
|
||||
or
|
||||
hasChiNodeAfterUninitializedGroup(vvar, primaryInstr)
|
||||
)
|
||||
or
|
||||
exists(Alias::VariableGroup vg |
|
||||
instr = uninitializedGroup(vg) and
|
||||
result = vg.getType()
|
||||
)
|
||||
or
|
||||
instr = reusedPhiInstruction(_) and
|
||||
@@ -448,6 +691,8 @@ private module Cached {
|
||||
or
|
||||
instr = chiInstruction(_) and opcode instanceof Opcode::Chi
|
||||
or
|
||||
instr = uninitializedGroup(_) and opcode instanceof Opcode::UninitializedGroup
|
||||
or
|
||||
instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
@@ -460,10 +705,15 @@ private module Cached {
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction primaryInstr |
|
||||
exists(Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
exists(Alias::VariableGroup vg |
|
||||
instr = uninitializedGroup(vg) and
|
||||
result = vg.getIRFunction()
|
||||
)
|
||||
or
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
@@ -478,6 +728,8 @@ private module Cached {
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
or
|
||||
instruction = getChi(result.(UninitializedGroupInstruction))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +737,7 @@ private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
private ChiInstruction getChi(Instruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
@@ -506,6 +758,16 @@ private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasChiNodeAfterUninitializedGroup(
|
||||
Alias::AliasedVirtualVariable vvar, UninitializedGroupInstruction initGroup
|
||||
) {
|
||||
exists(Alias::GroupedMemoryLocation defLocation |
|
||||
initGroup = uninitializedGroup(defLocation.getGroup()) and
|
||||
defLocation.getVirtualVariable() = vvar and
|
||||
Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap
|
||||
)
|
||||
}
|
||||
|
||||
private import PhiInsertion
|
||||
|
||||
/**
|
||||
@@ -668,19 +930,37 @@ private import DefUse
|
||||
* potentially very sparse.
|
||||
*/
|
||||
module DefUse {
|
||||
bindingset[index, block]
|
||||
pragma[inline_late]
|
||||
private int getNonChiOffset(int index, OldBlock block) {
|
||||
exists(OldIR::IRFunction func, Instruction i, OldBlock entryBlock |
|
||||
func = block.getEnclosingIRFunction() and
|
||||
i = block.getInstruction(index) and
|
||||
entryBlock = func.getEntryBlock()
|
||||
|
|
||||
if
|
||||
block = entryBlock and
|
||||
not i instanceof InitializeNonLocalInstruction and
|
||||
not i instanceof AliasedDefinitionInstruction
|
||||
then result = 2 * (index + count(VariableGroup vg | vg.getIRFunction() = func))
|
||||
else result = 2 * index
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[index, block]
|
||||
pragma[inline_late]
|
||||
private int getChiOffset(int index, OldBlock block) { result = getNonChiOffset(index, block) + 1 }
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` for the definition at offset `defOffset` in block `defBlock`.
|
||||
*/
|
||||
Instruction getDefinitionOrChiInstruction(
|
||||
private Instruction getDefinitionOrChiInstruction0(
|
||||
OldBlock defBlock, int defOffset, Alias::MemoryLocation defLocation,
|
||||
Alias::MemoryLocation actualDefLocation
|
||||
) {
|
||||
exists(OldInstruction oldInstr, int oldOffset |
|
||||
oldInstr = defBlock.getInstruction(oldOffset) and
|
||||
oldOffset >= 0
|
||||
|
|
||||
exists(OldInstruction oldInstr, int oldOffset | oldInstr = defBlock.getInstruction(oldOffset) |
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
defOffset = getChiOffset(oldOffset, defBlock) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
@@ -689,7 +969,7 @@ module DefUse {
|
||||
actualDefLocation = defLocation.getVirtualVariable()
|
||||
or
|
||||
// An even offset corresponds to the original instruction.
|
||||
defOffset = oldOffset * 2 and
|
||||
defOffset = getNonChiOffset(oldOffset, defBlock) and
|
||||
result = getNewInstruction(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
@@ -702,6 +982,54 @@ module DefUse {
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
or
|
||||
exists(
|
||||
Alias::VariableGroup vg, int index, UninitializedGroupInstruction initGroup,
|
||||
Alias::GroupedMemoryLocation gml
|
||||
|
|
||||
// Add 3 to account for the function prologue:
|
||||
// v1(void) = EnterFunction
|
||||
// m1(unknown) = AliasedDefinition
|
||||
// m2(unknown) = InitializeNonLocal
|
||||
index = 3 + vg.getInitializationOrder() and
|
||||
not gml.isMayAccess() and
|
||||
gml.isSome() and
|
||||
gml.getGroup() = vg and
|
||||
vg.getIRFunction().getEntryBlock() = defBlock and
|
||||
initGroup = uninitializedGroup(vg) and
|
||||
(defLocation = gml or defLocation = gml.getVirtualVariable())
|
||||
|
|
||||
result = initGroup and
|
||||
defOffset = 2 * index and
|
||||
actualDefLocation = defLocation
|
||||
or
|
||||
result = getChi(initGroup) and
|
||||
defOffset = 2 * index + 1 and
|
||||
actualDefLocation = defLocation.getVirtualVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private ChiInstruction remapGetDefinitionOrChiInstruction(Instruction oldResult) {
|
||||
exists(IRFunction func |
|
||||
isFirstInstructionBeforeUninitializedGroup(oldResult, func) and
|
||||
isLastInstructionForUninitializedGroups(result, func)
|
||||
)
|
||||
}
|
||||
|
||||
Instruction getDefinitionOrChiInstruction(
|
||||
OldBlock defBlock, int defOffset, Alias::MemoryLocation defLocation,
|
||||
Alias::MemoryLocation actualDefLocation
|
||||
) {
|
||||
exists(Instruction oldResult |
|
||||
oldResult =
|
||||
getDefinitionOrChiInstruction0(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
(
|
||||
result = remapGetDefinitionOrChiInstruction(oldResult)
|
||||
or
|
||||
not exists(remapGetDefinitionOrChiInstruction(oldResult)) and
|
||||
result = oldResult
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -842,8 +1170,20 @@ module DefUse {
|
||||
block.getInstruction(index) = def and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation) and
|
||||
if overlap instanceof MayPartiallyOverlap
|
||||
then offset = (index * 2) + 1 // The use will be connected to the definition on the `Chi` instruction.
|
||||
else offset = index * 2 // The use will be connected to the definition on the original instruction.
|
||||
then offset = getChiOffset(index, block) // The use will be connected to the definition on the `Chi` instruction.
|
||||
else offset = getNonChiOffset(index, block) // The use will be connected to the definition on the original instruction.
|
||||
)
|
||||
or
|
||||
exists(UninitializedGroupInstruction initGroup, int index, Overlap overlap, VariableGroup vg |
|
||||
initGroup.getEnclosingIRFunction().getEntryBlock() = getNewBlock(block) and
|
||||
vg = defLocation.(Alias::GroupedMemoryLocation).getGroup() and
|
||||
// EnterFunction + AliasedDefinition + InitializeNonLocal + index
|
||||
index = 3 + vg.getInitializationOrder() and
|
||||
initGroup = uninitializedGroup(vg) and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation) and
|
||||
if overlap instanceof MayPartiallyOverlap and hasChiNodeAfterUninitializedGroup(initGroup)
|
||||
then offset = 2 * index + 1 // The use will be connected to the definition on the `Chi` instruction.
|
||||
else offset = 2 * index // The use will be connected to the definition on the original instruction.
|
||||
)
|
||||
}
|
||||
|
||||
@@ -904,10 +1244,11 @@ module DefUse {
|
||||
block.getInstruction(index) = use and
|
||||
(
|
||||
// A direct use of the location.
|
||||
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and offset = index * 2
|
||||
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and
|
||||
offset = getNonChiOffset(index, block)
|
||||
or
|
||||
// A `Chi` instruction will include a use of the virtual variable.
|
||||
hasChiNode(useLocation, use) and offset = (index * 2) + 1
|
||||
hasChiNode(useLocation, use) and offset = getChiOffset(index, block)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1057,5 +1398,9 @@ module Ssa {
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasChiNodeAfterUninitializedGroup = Cached::hasChiNodeAfterUninitializedGroup/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
|
||||
class VariableGroup = Alias::VariableGroup;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import AliasAnalysis
|
||||
private import SimpleSSAImports
|
||||
import SimpleSSAPublicImports
|
||||
private import AliasConfiguration
|
||||
import AliasConfiguration
|
||||
private import codeql.util.Unit
|
||||
|
||||
private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRType type) {
|
||||
exists(Instruction constantBase, int bitOffset |
|
||||
@@ -48,7 +49,7 @@ predicate canReuseSsaForVariable(IRAutomaticVariable var) {
|
||||
|
||||
private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) }
|
||||
|
||||
private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var }
|
||||
private MemoryLocation getMemoryLocation(Allocation var) { result.getAnAllocation() = var }
|
||||
|
||||
class MemoryLocation extends TMemoryLocation {
|
||||
Allocation var;
|
||||
@@ -57,7 +58,7 @@ class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
final string toString() { result = var.getAllocationString() }
|
||||
|
||||
final Allocation getAllocation() { result = var }
|
||||
final Allocation getAnAllocation() { result = var }
|
||||
|
||||
final Language::Location getLocation() { result = var.getLocation() }
|
||||
|
||||
@@ -77,6 +78,40 @@ class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
predicate canReuseSsaForOldResult(Instruction instr) { none() }
|
||||
|
||||
abstract class VariableGroup extends Unit {
|
||||
abstract Allocation getAnAllocation();
|
||||
|
||||
string toString() { result = "{" + strictconcat(this.getAnAllocation().toString(), ", ") + "}" }
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
|
||||
abstract IRFunction getIRFunction();
|
||||
|
||||
abstract Language::LanguageType getType();
|
||||
|
||||
abstract int getInitializationOrder();
|
||||
}
|
||||
|
||||
class GroupedMemoryLocation extends MemoryLocation {
|
||||
VariableGroup vg;
|
||||
|
||||
GroupedMemoryLocation() { none() }
|
||||
|
||||
/** Gets an allocation of this memory location. */
|
||||
Allocation getAnAllocation() { result = vg.getAnAllocation() }
|
||||
|
||||
/** Gets the set of allocations associated with this memory location. */
|
||||
VariableGroup getGroup() { result = vg }
|
||||
|
||||
predicate isMayAccess() { none() }
|
||||
|
||||
/** Holds if this memory location represents all the enclosing allocations. */
|
||||
predicate isAll() { none() }
|
||||
|
||||
/** Holds if this memory location represents one or more of the enclosing allocations. */
|
||||
predicate isSome() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a set of `MemoryLocation`s that cannot overlap with
|
||||
* `MemoryLocation`s outside of the set. The `VirtualVariable` will be
|
||||
|
||||
@@ -27,6 +27,11 @@ private import implementations.StdPair
|
||||
private import implementations.StdMap
|
||||
private import implementations.StdSet
|
||||
private import implementations.StdString
|
||||
private import implementations.StdFunction
|
||||
private import implementations.StdException
|
||||
private import implementations.StdAllocator
|
||||
private import implementations.StdAlgorithm
|
||||
private import implementations.StdMath
|
||||
private import implementations.Swap
|
||||
private import implementations.GetDelim
|
||||
private import implementations.SmartPointer
|
||||
|
||||
@@ -7,119 +7,6 @@
|
||||
import semmle.code.cpp.models.interfaces.Allocation
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* An allocation function (such as `malloc`) that has an argument for the size
|
||||
* in bytes.
|
||||
*/
|
||||
private class MallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
|
||||
MallocAllocationFunction() {
|
||||
// --- C library allocation
|
||||
this.hasGlobalOrStdOrBslName("malloc") and // malloc(size)
|
||||
sizeArg = 0
|
||||
or
|
||||
this.hasGlobalName([
|
||||
// --- Windows Memory Management for Windows Drivers
|
||||
"MmAllocateContiguousMemory", // MmAllocateContiguousMemory(size, maxaddress)
|
||||
"MmAllocateContiguousNodeMemory", // MmAllocateContiguousNodeMemory(size, minaddress, maxaddress, bound, flag, prefer)
|
||||
"MmAllocateContiguousMemorySpecifyCache", // MmAllocateContiguousMemorySpecifyCache(size, minaddress, maxaddress, bound, type)
|
||||
"MmAllocateContiguousMemorySpecifyCacheNode", // MmAllocateContiguousMemorySpecifyCacheNode(size, minaddress, maxaddress, bound, type, prefer)
|
||||
"MmAllocateNonCachedMemory", // MmAllocateNonCachedMemory(size)
|
||||
"MmAllocateMappingAddress", // MmAllocateMappingAddress(size, tag)
|
||||
// --- Windows COM allocation
|
||||
"CoTaskMemAlloc", // CoTaskMemAlloc(size)
|
||||
// --- Solaris/BSD kernel memory allocator
|
||||
"kmem_alloc", // kmem_alloc(size, flags)
|
||||
"kmem_zalloc", // kmem_zalloc(size, flags)
|
||||
// --- OpenSSL memory allocation
|
||||
"CRYPTO_malloc", // CRYPTO_malloc(size_t num, const char *file, int line)
|
||||
"CRYPTO_zalloc", // CRYPTO_zalloc(size_t num, const char *file, int line)
|
||||
"CRYPTO_secure_malloc", // CRYPTO_secure_malloc(size_t num, const char *file, int line)
|
||||
"CRYPTO_secure_zalloc", // CRYPTO_secure_zalloc(size_t num, const char *file, int line)
|
||||
"g_malloc", // g_malloc (n_bytes);
|
||||
"g_try_malloc" // g_try_malloc(n_bytes);
|
||||
]) and
|
||||
sizeArg = 0
|
||||
or
|
||||
this.hasGlobalName([
|
||||
// --- Windows Memory Management for Windows Drivers
|
||||
"ExAllocatePool", // ExAllocatePool(type, size)
|
||||
"ExAllocatePool2", // ExAllocatePool2(flags, size, tag)
|
||||
"ExAllocatePool3", // ExAllocatePool3(flags, size, tag, extparams, extparamscount)
|
||||
"ExAllocatePoolWithTag", // ExAllocatePool(type, size, tag)
|
||||
"ExAllocatePoolWithTagPriority", // ExAllocatePoolWithTagPriority(type, size, tag, priority)
|
||||
"ExAllocatePoolWithQuota", // ExAllocatePoolWithQuota(type, size)
|
||||
"ExAllocatePoolWithQuotaTag", // ExAllocatePoolWithQuotaTag(type, size, tag)
|
||||
"ExAllocatePoolZero", // ExAllocatePoolZero(type, size, tag)
|
||||
"IoAllocateMdl", // IoAllocateMdl(address, size, flag, flag, irp)
|
||||
"IoAllocateErrorLogEntry", // IoAllocateErrorLogEntry(object, size)
|
||||
// --- Windows Global / Local legacy allocation
|
||||
"LocalAlloc", // LocalAlloc(flags, size)
|
||||
"GlobalAlloc", // GlobalAlloc(flags, size)
|
||||
// --- Windows System Services allocation
|
||||
"VirtualAlloc" // VirtualAlloc(address, size, type, flag)
|
||||
]) and
|
||||
sizeArg = 1
|
||||
or
|
||||
this.hasGlobalName("HeapAlloc") and // HeapAlloc(heap, flags, size)
|
||||
sizeArg = 2
|
||||
or
|
||||
this.hasGlobalName([
|
||||
// --- Windows Memory Management for Windows Drivers
|
||||
"MmAllocatePagesForMdl", // MmAllocatePagesForMdl(minaddress, maxaddress, skip, size)
|
||||
"MmAllocatePagesForMdlEx", // MmAllocatePagesForMdlEx(minaddress, maxaddress, skip, size, type, flags)
|
||||
"MmAllocateNodePagesForMdlEx" // MmAllocateNodePagesForMdlEx(minaddress, maxaddress, skip, size, type, prefer, flags)
|
||||
]) and
|
||||
sizeArg = 3
|
||||
}
|
||||
|
||||
override int getSizeArg() { result = sizeArg }
|
||||
}
|
||||
|
||||
/**
|
||||
* An allocation function (such as `alloca`) that does not require a
|
||||
* corresponding free (and has an argument for the size in bytes).
|
||||
*/
|
||||
private class AllocaAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
|
||||
AllocaAllocationFunction() {
|
||||
this.hasGlobalName([
|
||||
// --- stack allocation
|
||||
"alloca", // // alloca(size)
|
||||
"__builtin_alloca", // __builtin_alloca(size)
|
||||
"_alloca", // _alloca(size)
|
||||
"_malloca" // _malloca(size)
|
||||
]) and
|
||||
sizeArg = 0
|
||||
}
|
||||
|
||||
override int getSizeArg() { result = sizeArg }
|
||||
|
||||
override predicate requiresDealloc() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An allocation function (such as `calloc`) that has an argument for the size
|
||||
* and another argument for the size of those units (in bytes).
|
||||
*/
|
||||
private class CallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
int multArg;
|
||||
|
||||
CallocAllocationFunction() {
|
||||
// --- C library allocation
|
||||
this.hasGlobalOrStdOrBslName("calloc") and // calloc(num, size)
|
||||
sizeArg = 1 and
|
||||
multArg = 0
|
||||
}
|
||||
|
||||
override int getSizeArg() { result = sizeArg }
|
||||
|
||||
override int getSizeMult() { result = multArg }
|
||||
}
|
||||
|
||||
/**
|
||||
* An allocation function (such as `realloc`) that has an argument for the size
|
||||
* in bytes, and an argument for an existing pointer that is to be reallocated.
|
||||
@@ -373,6 +260,63 @@ private class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
|
||||
override predicate requiresDealloc() { not exists(this.getPlacementPointer()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is an allocation function according to the
|
||||
* extensible `allocationFunctionModel` predicate.
|
||||
*/
|
||||
private predicate isAllocationFunctionFromModel(
|
||||
Function f, string namespace, string type, string name
|
||||
) {
|
||||
exists(boolean subtypes | allocationFunctionModel(namespace, type, subtypes, name, _, _, _, _) |
|
||||
if type = ""
|
||||
then f.hasQualifiedName(namespace, "", name)
|
||||
else
|
||||
exists(Class c |
|
||||
c.hasQualifiedName(namespace, type) and f.hasQualifiedName(namespace, _, name)
|
||||
|
|
||||
if subtypes = true
|
||||
then f = c.getADerivedClass*().getAMemberFunction()
|
||||
else f = c.getAMemberFunction()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An allocation function modeled via the extensible `allocationFunctionModel` predicate.
|
||||
*/
|
||||
private class AllocationFunctionFromModel extends AllocationFunction {
|
||||
string namespace;
|
||||
string type;
|
||||
string name;
|
||||
|
||||
AllocationFunctionFromModel() { isAllocationFunctionFromModel(this, namespace, type, name) }
|
||||
|
||||
final override int getSizeArg() {
|
||||
exists(string sizeArg |
|
||||
allocationFunctionModel(namespace, type, _, name, sizeArg, _, _, _) and
|
||||
result = sizeArg.toInt()
|
||||
)
|
||||
}
|
||||
|
||||
final override int getSizeMult() {
|
||||
exists(string sizeMult |
|
||||
allocationFunctionModel(namespace, type, _, name, _, sizeMult, _, _) and
|
||||
result = sizeMult.toInt()
|
||||
)
|
||||
}
|
||||
|
||||
final override int getReallocPtrArg() {
|
||||
exists(string reallocPtrArg |
|
||||
allocationFunctionModel(namespace, type, _, name, _, _, reallocPtrArg, _) and
|
||||
result = reallocPtrArg.toInt()
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate requiresDealloc() {
|
||||
allocationFunctionModel(namespace, type, _, name, _, _, _, true)
|
||||
}
|
||||
}
|
||||
|
||||
private module HeuristicAllocation {
|
||||
/** A class that maps an `AllocationExpr` to an `HeuristicAllocationExpr`. */
|
||||
private class HeuristicAllocationModeled extends HeuristicAllocationExpr instanceof AllocationExpr
|
||||
|
||||
@@ -7,61 +7,42 @@
|
||||
import semmle.code.cpp.models.interfaces.Deallocation
|
||||
|
||||
/**
|
||||
* A deallocation function such as `free`.
|
||||
* Holds if `f` is an deallocation function according to the
|
||||
* extensible `deallocationFunctionModel` predicate.
|
||||
*/
|
||||
private class StandardDeallocationFunction extends DeallocationFunction {
|
||||
int freedArg;
|
||||
private predicate isDeallocationFunctionFromModel(
|
||||
Function f, string namespace, string type, string name
|
||||
) {
|
||||
exists(boolean subtypes | deallocationFunctionModel(namespace, type, subtypes, name, _) |
|
||||
if type = ""
|
||||
then f.hasQualifiedName(namespace, "", name)
|
||||
else
|
||||
exists(Class c |
|
||||
c.hasQualifiedName(namespace, type) and f.hasQualifiedName(namespace, _, name)
|
||||
|
|
||||
if subtypes = true
|
||||
then f = c.getADerivedClass*().getAMemberFunction()
|
||||
else f = c.getAMemberFunction()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
StandardDeallocationFunction() {
|
||||
this.hasGlobalOrStdOrBslName([
|
||||
// --- C library allocation
|
||||
"free", "realloc"
|
||||
]) and
|
||||
freedArg = 0
|
||||
or
|
||||
this.hasGlobalName([
|
||||
// --- OpenSSL memory deallocation
|
||||
"CRYPTO_free", "CRYPTO_secure_free",
|
||||
// --- glib memory deallocation
|
||||
"g_free"
|
||||
]) and
|
||||
freedArg = 0
|
||||
or
|
||||
this.hasGlobalOrStdName([
|
||||
// --- Windows Memory Management for Windows Drivers
|
||||
"ExFreePool", "ExFreePoolWithTag", "ExDeleteTimer", "IoFreeIrp", "IoFreeMdl",
|
||||
"IoFreeErrorLogEntry", "IoFreeWorkItem", "MmFreeContiguousMemory",
|
||||
"MmFreeContiguousMemorySpecifyCache", "MmFreeNonCachedMemory", "MmFreeMappingAddress",
|
||||
"MmFreePagesFromMdl", "MmUnmapReservedMapping", "MmUnmapLockedPages",
|
||||
"NdisFreeGenericObject", "NdisFreeMemory", "NdisFreeMemoryWithTag", "NdisFreeMdl",
|
||||
"NdisFreeNetBufferListPool", "NdisFreeNetBufferPool",
|
||||
// --- Windows Global / Local legacy allocation
|
||||
"LocalFree", "GlobalFree", "LocalReAlloc", "GlobalReAlloc",
|
||||
// --- Windows System Services allocation
|
||||
"VirtualFree",
|
||||
// --- Windows COM allocation
|
||||
"CoTaskMemFree", "CoTaskMemRealloc",
|
||||
// --- Windows Automation
|
||||
"SysFreeString",
|
||||
// --- Solaris/BSD kernel memory allocator
|
||||
"kmem_free"
|
||||
]) and
|
||||
freedArg = 0
|
||||
or
|
||||
this.hasGlobalOrStdName([
|
||||
// --- Windows Memory Management for Windows Drivers
|
||||
"ExFreeToLookasideListEx", "ExFreeToPagedLookasideList", "ExFreeToNPagedLookasideList",
|
||||
"NdisFreeMemoryWithTagPriority", "StorPortFreeMdl", "StorPortFreePool",
|
||||
// --- NetBSD pool manager
|
||||
"pool_put", "pool_cache_put"
|
||||
]) and
|
||||
freedArg = 1
|
||||
or
|
||||
this.hasGlobalOrStdName(["HeapFree", "HeapReAlloc"]) and
|
||||
freedArg = 2
|
||||
/**
|
||||
* A deallocation function modeled via the extensible `deallocationFunctionModel` predicate.
|
||||
*/
|
||||
private class DeallocationFunctionFromModel extends DeallocationFunction {
|
||||
string namespace;
|
||||
string type;
|
||||
string name;
|
||||
|
||||
DeallocationFunctionFromModel() { isDeallocationFunctionFromModel(this, namespace, type, name) }
|
||||
|
||||
final override int getFreedArg() {
|
||||
exists(string freedArg |
|
||||
deallocationFunctionModel(namespace, type, _, name, freedArg) and
|
||||
result = freedArg.toInt()
|
||||
)
|
||||
}
|
||||
|
||||
override int getFreedArg() { result = freedArg }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -86,6 +86,41 @@ private class StdIterator extends Iterator, Class {
|
||||
override Type getValueType() { result = this.getTemplateArgument(1).(Type).getUnderlyingType() }
|
||||
}
|
||||
|
||||
private class StdReverseIterator extends Iterator, Class {
|
||||
StdReverseIterator() { this.hasQualifiedName(["std", "bsl"], "reverse_iterator") }
|
||||
|
||||
override Type getValueType() { result = this.getTemplateArgument(1).(Type).getUnderlyingType() }
|
||||
}
|
||||
|
||||
private class StdIstreamBufIterator extends Iterator, Class {
|
||||
StdIstreamBufIterator() { this.hasQualifiedName(["std", "bsl"], "istreambuf_iterator") }
|
||||
|
||||
override Type getValueType() { result = this.getTemplateArgument(1).(Type).getUnderlyingType() }
|
||||
}
|
||||
|
||||
private class StdIstreambufIteratorConstructor extends Constructor, SideEffectFunction,
|
||||
AliasFunction
|
||||
{
|
||||
StdIstreambufIteratorConstructor() { this.getDeclaringType() instanceof StdIstreamBufIterator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `FunctionInput` corresponding to an iterator parameter to
|
||||
* user-defined operator `op`, at `index`.
|
||||
@@ -579,23 +614,43 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = -1 }
|
||||
}
|
||||
|
||||
private string beginName() {
|
||||
result = ["begin", "cbegin", "rbegin", "crbegin", "before_begin", "cbefore_begin"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A `begin` member function, or a related function, that returns an iterator.
|
||||
*/
|
||||
class BeginFunction extends MemberFunction {
|
||||
class BeginFunction extends Function {
|
||||
BeginFunction() {
|
||||
this.hasName(["begin", "cbegin", "rbegin", "crbegin", "before_begin", "cbefore_begin"]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
this.getUnspecifiedType() instanceof Iterator and
|
||||
(
|
||||
this.hasName(beginName()) and
|
||||
this instanceof MemberFunction
|
||||
or
|
||||
this.hasGlobalOrStdOrBslName(beginName()) and
|
||||
not this instanceof MemberFunction and
|
||||
this.getNumberOfParameters() = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private string endName() { result = ["end", "cend", "rend", "crend"] }
|
||||
|
||||
/**
|
||||
* An `end` member function, or a related function, that returns an iterator.
|
||||
*/
|
||||
class EndFunction extends MemberFunction {
|
||||
class EndFunction extends Function {
|
||||
EndFunction() {
|
||||
this.hasName(["end", "cend", "rend", "crend"]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
this.getUnspecifiedType() instanceof Iterator and
|
||||
(
|
||||
this.hasName(endName()) and
|
||||
this instanceof MemberFunction
|
||||
or
|
||||
this.hasGlobalOrStdOrBslName(endName()) and
|
||||
this instanceof MemberFunction and
|
||||
this.getNumberOfParameters() = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,7 +658,7 @@ class EndFunction extends MemberFunction {
|
||||
* A `begin` or `end` member function, or a related member function, that
|
||||
* returns an iterator.
|
||||
*/
|
||||
class BeginOrEndFunction extends MemberFunction {
|
||||
class BeginOrEndFunction extends Function {
|
||||
BeginOrEndFunction() {
|
||||
this instanceof BeginFunction or
|
||||
this instanceof EndFunction
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard functions `printf`, `wprintf` and their glib variants.
|
||||
@@ -96,7 +97,7 @@ private class Sprintf extends FormattingFunction {
|
||||
/**
|
||||
* Implements `Snprintf`.
|
||||
*/
|
||||
private class SnprintfImpl extends Snprintf {
|
||||
private class SnprintfImpl extends Snprintf, AliasFunction, SideEffectFunction {
|
||||
SnprintfImpl() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
@@ -143,6 +144,26 @@ private class SnprintfImpl extends Snprintf {
|
||||
}
|
||||
|
||||
override int getSizeParameterIndex() { result = 1 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
// We don't know how many parameters are passed to the function since it's varargs, but they also don't escape.
|
||||
index = this.getFormatParameterIndex()
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = this.getOutputParameterIndex(false) and buffer = true and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
// We don't know how many parameters are passed to the function since it's varargs, but they also have read side effects.
|
||||
i = this.getFormatParameterIndex() and buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Provides models for C++ functions from the `algorithms` header.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
private class StdPartialSort extends Function, SideEffectFunction, AliasFunction {
|
||||
StdPartialSort() { this.hasGlobalOrStdName("partial_sort") }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = this.getAnIteratorParameterIndex() and buffer = true and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = this.getAnIteratorParameterIndex() and
|
||||
buffer = true and
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
}
|
||||
|
||||
private int getAnIteratorParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
index = this.getAnIteratorParameterIndex()
|
||||
or
|
||||
this.getParameter(index).getUnspecifiedType() instanceof ReferenceType
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
}
|
||||
|
||||
private class StdSortHeap extends Function, SideEffectFunction, AliasFunction {
|
||||
StdSortHeap() { this.hasGlobalOrStdName("sort_heap") }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = this.getAnIteratorParameterIndex() and buffer = true and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = this.getAnIteratorParameterIndex() and
|
||||
buffer = true
|
||||
}
|
||||
|
||||
private int getAnIteratorParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = this.getAnIteratorParameterIndex() }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
}
|
||||
|
||||
private class StdGenerateN extends Function, SideEffectFunction, AliasFunction {
|
||||
StdGenerateN() { this.hasGlobalOrStdName("generate_n") }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = this.getAnIteratorParameterIndex() and buffer = true and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and buffer = false
|
||||
}
|
||||
|
||||
private int getAnIteratorParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = this.getAnIteratorParameterIndex() }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
}
|
||||
|
||||
private class StdFindIfOrIfNot extends Function, SideEffectFunction, AliasFunction {
|
||||
StdFindIfOrIfNot() { this.hasGlobalOrStdName(["find_if", "find_if_not"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = this.getAnIteratorParameterIndex() and buffer = true
|
||||
or
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and buffer = false
|
||||
}
|
||||
|
||||
private int getAnIteratorParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
this.getParameter(index).getUnspecifiedType() instanceof ReferenceType
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||
index = this.getAnIteratorParameterIndex()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Provides models for C++ `allocator` and `allocator_traits` classes.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
/** The `std::allocator` class. */
|
||||
class StdAllocator extends Class {
|
||||
StdAllocator() { this.hasGlobalOrStdOrBslName("allocator") }
|
||||
}
|
||||
|
||||
/** The `std::allocator_traits` class. */
|
||||
class StdAllocatorTraits extends Class {
|
||||
StdAllocatorTraits() { this.hasGlobalOrStdOrBslName("allocator_traits") }
|
||||
}
|
||||
|
||||
private class StdAllocatorConstructor extends Constructor, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorConstructor() { this.getDeclaringType() instanceof StdAllocator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdAllocatorDestructor extends Destructor, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorDestructor() { this.getDeclaringType() instanceof StdAllocator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
}
|
||||
|
||||
private class StdAllocatorAddress extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorAddress() { this.getClassAndName("address") instanceof StdAllocator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class StdAllocatorAllocate extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorAllocate() { this.getClassAndName("allocate") instanceof StdAllocator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class StdAllocatorTraitsAllocate extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorTraitsAllocate() {
|
||||
this.getClassAndName(["allocate", "allocate_at_least"]) instanceof StdAllocatorTraits
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
this.getParameter(index).getUnspecifiedType() instanceof ReferenceType
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdAllocatorDeallocate extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorDeallocate() { this.getClassAndName("deallocate") instanceof StdAllocator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and
|
||||
buffer = false and
|
||||
mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdAllocatorTraitsDeallocate extends MemberFunction, AliasFunction, SideEffectFunction
|
||||
{
|
||||
StdAllocatorTraitsDeallocate() {
|
||||
this.getClassAndName("deallocate") instanceof StdAllocatorTraits
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = [0, 1] }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 1 and
|
||||
buffer = false and
|
||||
mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = [0, 1] and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdAllocatorMaxSize extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorMaxSize() { this.getClassAndName("max_size") instanceof StdAllocator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class StdAllocatTraitsMaxSize extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatTraitsMaxSize() { this.getClassAndName("max_size") instanceof StdAllocatorTraits }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class StdAllocatorConstruct extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorConstruct() { this.getClassAndName("construct") instanceof StdAllocator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
class StdAllocatorTraitsConstruct extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorTraitsConstruct() { this.getClassAndName("construct") instanceof StdAllocatorTraits }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
index = 1 or this.getParameter(index).getUnspecifiedType() instanceof ReferenceType
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
class StdAllocatorDestroy extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorDestroy() { this.getClassAndName("destroy") instanceof StdAllocator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
class StdAllocatorTraitsDestroy extends MemberFunction, AliasFunction, SideEffectFunction {
|
||||
StdAllocatorTraitsDestroy() { this.getClassAndName("destroy") instanceof StdAllocatorTraits }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = [0, 1] }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = false
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@
|
||||
* Provides models for C++ containers `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
/**
|
||||
* A sequence container template class (for example, `std::vector`) from the
|
||||
@@ -55,77 +57,10 @@ private class Vector extends StdSequenceContainer {
|
||||
Vector() { this.hasQualifiedName(["std", "bsl"], "vector") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional model for standard container constructors that reference the
|
||||
* value type of the container (that is, the `T` in `std::vector<T>`). For
|
||||
* example the fill constructor:
|
||||
* ```
|
||||
* std::vector<std::string> v(100, potentially_tainted_string);
|
||||
* ```
|
||||
*/
|
||||
private class StdSequenceContainerConstructor extends Constructor, TaintFunction {
|
||||
StdSequenceContainerConstructor() {
|
||||
this.getDeclaringType() instanceof Vector or
|
||||
this.getDeclaringType() instanceof Deque or
|
||||
this.getDeclaringType() instanceof List or
|
||||
this.getDeclaringType() instanceof ForwardList
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to the
|
||||
* value type of the container.
|
||||
*/
|
||||
int getAValueTypeParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from any parameter of the value type to the returned object
|
||||
(
|
||||
input.isParameterDeref(this.getAValueTypeParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
|
||||
or
|
||||
output.isQualifierObject()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container function `data`.
|
||||
*/
|
||||
private class StdSequenceContainerData extends TaintFunction {
|
||||
StdSequenceContainerData() {
|
||||
this.getClassAndName("data") instanceof Array or
|
||||
this.getClassAndName("data") instanceof Vector
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from container itself (qualifier) to return value
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the qualifier (for writes to
|
||||
// `data`)
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `push_back` and `push_front`.
|
||||
*/
|
||||
class StdSequenceContainerPush extends MemberFunction {
|
||||
class StdSequenceContainerPush extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
StdSequenceContainerPush() {
|
||||
this.getClassAndName("push_back") instanceof Vector or
|
||||
this.getClassAndName(["push_back", "push_front"]) instanceof Deque or
|
||||
@@ -141,41 +76,115 @@ class StdSequenceContainerPush extends MemberFunction {
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerPushModel extends StdSequenceContainerPush, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to qualifier
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
// the `std::vector<bool>` specialization doesn't take a reference as a
|
||||
// parameter. So we need to check that the parameter is actually a
|
||||
// reference.
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `front` and `back`.
|
||||
*/
|
||||
private class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
StdSequenceContainerFrontBack() {
|
||||
this.getClassAndName(["front", "back"]) instanceof Array or
|
||||
this.getClassAndName(["front", "back"]) instanceof Deque or
|
||||
this.getClassAndName("front") instanceof ForwardList or
|
||||
this.getClassAndName(["front", "back"]) instanceof List or
|
||||
this.getClassAndName(["front", "back"]) instanceof Vector
|
||||
private class StdSequenceContainerPopFrontOrBack extends MemberFunction, SideEffectFunction,
|
||||
AliasFunction
|
||||
{
|
||||
StdSequenceContainerPopFrontOrBack() {
|
||||
this.getClassAndName("pop_back") instanceof Vector or
|
||||
this.getClassAndName("pop_front") instanceof ForwardList or
|
||||
this.getClassAndName(["pop_front", "pop_back"]) instanceof Deque or
|
||||
this.getClassAndName(["pop_front", "pop_back"]) instanceof List
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from object to returned reference
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerClear extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
StdSequenceContainerClear() {
|
||||
this.getClassAndName("clear") instanceof Vector or
|
||||
this.getClassAndName("clear") instanceof Deque or
|
||||
this.getClassAndName("clear") instanceof ForwardList or
|
||||
this.getClassAndName("clear") instanceof List
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdVectorReserve extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
StdVectorReserve() { this.getClassAndName("reserve") instanceof Vector }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `insert` and `insert_after`.
|
||||
*/
|
||||
class StdSequenceContainerInsert extends MemberFunction {
|
||||
class StdSequenceContainerInsert extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
StdSequenceContainerInsert() {
|
||||
this.getClassAndName("insert") instanceof Deque or
|
||||
this.getClassAndName("insert") instanceof List or
|
||||
@@ -196,83 +205,138 @@ class StdSequenceContainerInsert extends MemberFunction {
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerInsertModel extends StdSequenceContainerInsert, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to container itself (qualifier) and return value
|
||||
(
|
||||
input.isQualifierObject() or
|
||||
input.isParameterDeref(this.getAValueTypeParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValue()
|
||||
)
|
||||
private class StdSequenceContainerFrontBack extends MemberFunction, SideEffectFunction,
|
||||
AliasFunction
|
||||
{
|
||||
StdSequenceContainerFrontBack() {
|
||||
this.getClassAndName(["front", "back"]) instanceof Deque or
|
||||
this.getClassAndName(["front", "back"]) instanceof List or
|
||||
this.getClassAndName(["front", "back"]) instanceof Vector or
|
||||
// forward_list does not have a 'back' member function
|
||||
this.getClassAndName("front") instanceof ForwardList
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
/**
|
||||
* The standard container function `assign`.
|
||||
*/
|
||||
private class StdSequenceContainerAssign extends TaintFunction {
|
||||
StdSequenceContainerAssign() {
|
||||
this.getClassAndName("assign") instanceof Deque or
|
||||
this.getClassAndName("assign") instanceof ForwardList or
|
||||
this.getClassAndName("assign") instanceof List or
|
||||
this.getClassAndName("assign") instanceof Vector
|
||||
}
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to the
|
||||
* value type of the container.
|
||||
*/
|
||||
int getAValueTypeParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to container itself (qualifier)
|
||||
(
|
||||
input.isParameterDeref(this.getAValueTypeParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
output.isQualifierObject()
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `at` and `operator[]`.
|
||||
*/
|
||||
class StdSequenceContainerAt extends MemberFunction {
|
||||
class StdSequenceContainerAt extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
StdSequenceContainerAt() {
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Array or
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Vector
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerAtModel extends StdSequenceContainerAt, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the qualifier
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerMemberEquals extends MemberFunction, SideEffectFunction,
|
||||
AliasFunction
|
||||
{
|
||||
StdSequenceContainerMemberEquals() {
|
||||
this.getClassAndName("operator==") instanceof Array or
|
||||
this.getClassAndName("operator==") instanceof Deque or
|
||||
this.getClassAndName("operator==") instanceof Vector
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 or index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
or
|
||||
i = 0 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerEquals extends Function, SideEffectFunction, AliasFunction {
|
||||
StdSequenceContainerEquals() {
|
||||
this.hasGlobalOrStdOrBslName("operator==") and
|
||||
not this instanceof MemberFunction and
|
||||
this.getNumberOfParameters() = 2 and
|
||||
(
|
||||
this.getParameter(0).getUnspecifiedType().(ReferenceType).getBaseType() instanceof Vector and
|
||||
this.getParameter(1).getUnspecifiedType().(ReferenceType).getBaseType() instanceof Vector
|
||||
or
|
||||
this.getParameter(0).getUnspecifiedType().(ReferenceType).getBaseType() instanceof List and
|
||||
this.getParameter(1).getUnspecifiedType().(ReferenceType).getBaseType() instanceof List
|
||||
or
|
||||
this.getParameter(0).getUnspecifiedType().(ReferenceType).getBaseType() instanceof Deque and
|
||||
this.getParameter(1).getUnspecifiedType().(ReferenceType).getBaseType() instanceof Deque
|
||||
)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = [0, 1] and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,20 +361,6 @@ class StdSequenceEmplace extends MemberFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceEmplaceModel extends StdSequenceEmplace, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter except the position iterator to qualifier and return value
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
input.isParameterDeref([1 .. this.getNumberOfParameters() - 1]) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValue()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard vector `emplace` function.
|
||||
*/
|
||||
@@ -318,6 +368,115 @@ class StdVectorEmplace extends StdSequenceEmplace {
|
||||
StdVectorEmplace() { this.getDeclaringType() instanceof Vector }
|
||||
}
|
||||
|
||||
private class StdSequenceSize extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
StdSequenceSize() {
|
||||
this.getClassAndName("size") instanceof Vector
|
||||
or
|
||||
this.getClassAndName("size") instanceof List
|
||||
or
|
||||
this.getClassAndName("size") instanceof Deque
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceDestructor extends Destructor, SideEffectFunction, AliasFunction {
|
||||
StdSequenceDestructor() {
|
||||
this.getDeclaringType() instanceof Vector
|
||||
or
|
||||
this.getDeclaringType() instanceof List
|
||||
or
|
||||
this.getDeclaringType() instanceof Deque
|
||||
}
|
||||
|
||||
private Destructor getElementDestructor() {
|
||||
result.getDeclaringType() = this.getTemplateArgument(0)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() {
|
||||
this.getElementDestructor().(SideEffectFunction).hasOnlySpecificReadSideEffects()
|
||||
or
|
||||
not exists(this.getElementDestructor())
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() {
|
||||
this.getElementDestructor().(SideEffectFunction).hasOnlySpecificWriteSideEffects()
|
||||
or
|
||||
not exists(this.getElementDestructor())
|
||||
}
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceConstructor extends Constructor, SideEffectFunction, AliasFunction {
|
||||
StdSequenceConstructor() {
|
||||
this.getDeclaringType() instanceof Vector
|
||||
or
|
||||
this.getDeclaringType() instanceof List
|
||||
or
|
||||
this.getDeclaringType() instanceof Deque
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class InitializerList extends Class {
|
||||
InitializerList() { this.hasQualifiedName(["std", "bsl"], "initializer_list") }
|
||||
|
||||
Type getElementType() { result = this.getTemplateArgument(0) }
|
||||
}
|
||||
|
||||
private class InitializerListConstructor extends Constructor, SideEffectFunction, AliasFunction {
|
||||
InitializerListConstructor() { this.getDeclaringType() instanceof InitializerList }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard vector `emplace_back` function.
|
||||
*/
|
||||
@@ -340,17 +499,6 @@ class StdSequenceEmplaceBack extends MemberFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceEmplaceBackModel extends StdSequenceEmplaceBack, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter to qualifier
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
input.isParameterDeref([0 .. this.getNumberOfParameters() - 1]) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard vector `emplace_back` function.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Provides models for the C++ `std::exception` class and subclasses.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/** The `std::exception` class. */
|
||||
class StdException extends Class {
|
||||
StdException() { this.hasGlobalOrStdOrBslName("exception") }
|
||||
}
|
||||
|
||||
/** The `std::bad_alloc` class. */
|
||||
class StdBadAllocException extends Class {
|
||||
StdBadAllocException() { this.hasGlobalOrStdOrBslName("bad_alloc") }
|
||||
}
|
||||
|
||||
private class StdBadAllocExceptionConstructor extends Constructor, SideEffectFunction, AliasFunction
|
||||
{
|
||||
StdBadAllocExceptionConstructor() { this.getDeclaringType() instanceof StdBadAllocException }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and buffer = false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Provides models for C++ `std::function` class.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
/**
|
||||
* An instantiation of the `std::function` class template.
|
||||
*/
|
||||
class StdFunction extends ClassTemplateInstantiation {
|
||||
StdFunction() { this.hasGlobalOrStdOrBslName("function") }
|
||||
}
|
||||
|
||||
private class StdFunctionConstructor extends Constructor, SideEffectFunction, AliasFunction {
|
||||
StdFunctionConstructor() { this.getDeclaringType() instanceof StdFunction }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdFunctionDestructor extends Destructor, SideEffectFunction, AliasFunction {
|
||||
StdFunctionDestructor() { this.getDeclaringType() instanceof StdFunction }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
/**
|
||||
* The `std::map` and `std::unordered_map` template classes.
|
||||
@@ -16,7 +18,9 @@ private class MapOrUnorderedMap extends Class {
|
||||
/**
|
||||
* Additional model for map constructors using iterator inputs.
|
||||
*/
|
||||
private class StdMapConstructor extends Constructor, TaintFunction {
|
||||
private class StdMapConstructor extends Constructor, TaintFunction, AliasFunction,
|
||||
SideEffectFunction
|
||||
{
|
||||
StdMapConstructor() { this.getDeclaringType() instanceof MapOrUnorderedMap }
|
||||
|
||||
/**
|
||||
@@ -35,6 +39,23 @@ private class StdMapConstructor extends Constructor, TaintFunction {
|
||||
output.isQualifierObject()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +154,7 @@ class StdMapAt extends MemberFunction {
|
||||
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
|
||||
}
|
||||
|
||||
private class StdMapAtModels extends StdMapAt, TaintFunction {
|
||||
private class StdMapAtModels extends StdMapAt, TaintFunction, AliasFunction, SideEffectFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
input.isQualifierObject() and
|
||||
@@ -144,6 +165,18 @@ private class StdMapAtModels extends StdMapAt, TaintFunction {
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
@@ -187,3 +220,63 @@ private class StdMapEqualRange extends TaintFunction {
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
class StdMapDestructor extends Destructor, SideEffectFunction, AliasFunction {
|
||||
StdMapDestructor() { this.getDeclaringType() instanceof MapOrUnorderedMap }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdMapClear extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
StdMapClear() { this.getClassAndName("clear") instanceof MapOrUnorderedMap }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
class StdMapSize extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
StdMapSize() { this.getClassAndName("size") instanceof MapOrUnorderedMap }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
108
cpp/ql/lib/semmle/code/cpp/models/implementations/StdMath.qll
Normal file
108
cpp/ql/lib/semmle/code/cpp/models/implementations/StdMath.qll
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Provides models for C++ functions from the `cmath` header.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
private class LdExp extends Function, SideEffectFunction {
|
||||
LdExp() { this.hasGlobalOrStdOrBslName(["ldexp", "ldexpf", "ldexpl"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class Abs extends Function, SideEffectFunction {
|
||||
Abs() { this.hasGlobalOrStdOrBslName(["abs", "fabs", "fabsf", "fabsl", "llabs", "imaxabs"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class Div extends Function, SideEffectFunction {
|
||||
Div() { this.hasGlobalOrStdOrBslName(["div", "ldiv", "lldiv", "imaxdiv"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class FMod extends Function, SideEffectFunction {
|
||||
FMod() { this.hasGlobalOrStdOrBslName(["fmod", "fmodf", "fmodl"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class Remainder extends Function, SideEffectFunction {
|
||||
Remainder() { this.hasGlobalOrStdOrBslName(["remainder", "remainderf", "remainderl"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class Remquo extends Function, SideEffectFunction {
|
||||
Remquo() { this.hasGlobalOrStdOrBslName(["remquo", "remquof", "remquol"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof PointerType and
|
||||
buffer = false and
|
||||
mustWrite = true
|
||||
}
|
||||
}
|
||||
|
||||
private class Fma extends Function, SideEffectFunction {
|
||||
Fma() { this.hasGlobalOrStdOrBslName(["fma", "fmaf", "fmal"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class Fmax extends Function, SideEffectFunction {
|
||||
Fmax() { this.hasGlobalOrStdOrBslName(["fmax", "fmaxf", "fmaxl"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class Fmin extends Function, SideEffectFunction {
|
||||
Fmin() { this.hasGlobalOrStdOrBslName(["fmin", "fminf", "fminl"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class Fdim extends Function, SideEffectFunction {
|
||||
Fdim() { this.hasGlobalOrStdOrBslName(["fdim", "fdimf", "fdiml"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
private class Nan extends Function, SideEffectFunction, AliasFunction {
|
||||
Nan() { this.hasGlobalOrStdOrBslName(["nan", "nanf", "nanl"]) }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = true
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* An instantiation of `std::pair<T1, T2>`.
|
||||
@@ -37,7 +39,9 @@ class StdPairCopyishConstructor extends Constructor, TaintFunction {
|
||||
/**
|
||||
* Additional model for `std::pair` constructors.
|
||||
*/
|
||||
private class StdPairConstructor extends Constructor, TaintFunction {
|
||||
private class StdPairConstructor extends Constructor, TaintFunction, AliasFunction,
|
||||
SideEffectFunction
|
||||
{
|
||||
StdPairConstructor() { this.getDeclaringType() instanceof StdPair }
|
||||
|
||||
/**
|
||||
@@ -59,4 +63,77 @@ private class StdPairConstructor extends Constructor, TaintFunction {
|
||||
output.isQualifierObject()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
// All the constructor parameters are references with the exception of this one:
|
||||
// ```
|
||||
// template<class... Args1, class... Args2>
|
||||
// pair(std::piecewise_construct_t, std::tuple<Args1...> first_args, std::tuple<Args2...> second_args);
|
||||
// ```
|
||||
// So we need to check that the parameters are actually references
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdPairDestructor extends Destructor, AliasFunction, SideEffectFunction {
|
||||
StdPairDestructor() { this.getDeclaringType() instanceof StdPair }
|
||||
|
||||
private Type getFirstType() { result = this.getDeclaringType().getTemplateArgument(0) }
|
||||
|
||||
private Type getSecondType() { result = this.getDeclaringType().getTemplateArgument(0) }
|
||||
|
||||
private Type getAType() { result = [this.getFirstType(), this.getSecondType()] }
|
||||
|
||||
/**
|
||||
* Gets the destructor associated with the base type of this pair
|
||||
*/
|
||||
private Destructor getADestructor() { result.getDeclaringType() = this.getAType() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() {
|
||||
this.getADestructor().(SideEffectFunction).hasOnlySpecificReadSideEffects()
|
||||
or
|
||||
// If there's no declared destructor for the base type then it won't have
|
||||
// any strange read side effects.
|
||||
not exists(this.getADestructor())
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() {
|
||||
this.getADestructor().(SideEffectFunction).hasOnlySpecificWriteSideEffects()
|
||||
or
|
||||
// If there's no declared destructor for the base type then it won't have
|
||||
// any strange write side effects.
|
||||
not exists(this.getADestructor())
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
this.getADestructor().(AliasFunction).parameterNeverEscapes(index)
|
||||
or
|
||||
// If there's no declared destructor for the base type then it won't cause
|
||||
// anything to escape.
|
||||
not exists(this.getADestructor()) and
|
||||
index = -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
/**
|
||||
* The `std::basic_string` template class instantiations.
|
||||
@@ -66,7 +68,7 @@ abstract private class StdStringTaintFunction extends TaintFunction {
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
final int getAnIteratorParameterIndex() {
|
||||
this.getParameter(result).getType() instanceof Iterator
|
||||
this.getParameter(result).getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +80,9 @@ abstract private class StdStringTaintFunction extends TaintFunction {
|
||||
* std::string b(a.begin(), a.end());
|
||||
* ```
|
||||
*/
|
||||
private class StdStringConstructor extends Constructor, StdStringTaintFunction {
|
||||
private class StdStringConstructor extends Constructor, StdStringTaintFunction, SideEffectFunction,
|
||||
AliasFunction
|
||||
{
|
||||
StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -94,6 +98,42 @@ private class StdStringConstructor extends Constructor, StdStringTaintFunction {
|
||||
output.isQualifierObject()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
private class StdStringDestructor extends Destructor, SideEffectFunction, AliasFunction {
|
||||
StdStringDestructor() { this.getDeclaringType() instanceof StdBasicString }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +204,7 @@ private class StdStringFrontBack extends StdStringTaintFunction {
|
||||
/**
|
||||
* The (non-member) `std::string` function `operator+`.
|
||||
*/
|
||||
private class StdStringPlus extends StdStringTaintFunction {
|
||||
private class StdStringPlus extends StdStringTaintFunction, SideEffectFunction, AliasFunction {
|
||||
StdStringPlus() {
|
||||
this.hasQualifiedName(["std", "bsl"], "operator+") and
|
||||
this.getUnspecifiedType() instanceof StdBasicString
|
||||
@@ -178,6 +218,22 @@ private class StdStringPlus extends StdStringTaintFunction {
|
||||
) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.getParameter(i).getUnspecifiedType() instanceof ReferenceType and
|
||||
buffer = false
|
||||
or
|
||||
this.getParameter(i).getUnspecifiedType() instanceof PointerType and
|
||||
buffer = true
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = [0, 1] }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,7 +241,7 @@ private class StdStringPlus extends StdStringTaintFunction {
|
||||
* All of these functions combine the existing string with a new
|
||||
* string (or character) from one of the arguments.
|
||||
*/
|
||||
private class StdStringAppend extends StdStringTaintFunction {
|
||||
private class StdStringAppend extends StdStringTaintFunction, SideEffectFunction, AliasFunction {
|
||||
StdStringAppend() {
|
||||
this.getClassAndName(["operator+=", "append", "replace"]) instanceof StdBasicString
|
||||
}
|
||||
@@ -210,6 +266,22 @@ private class StdStringAppend extends StdStringTaintFunction {
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and mustWrite = false and buffer = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = [-1, 0] and buffer = true
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = [-1, 0] }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,7 +373,7 @@ private class StdStringSubstr extends StdStringTaintFunction {
|
||||
/**
|
||||
* The `std::string` functions `at` and `operator[]`.
|
||||
*/
|
||||
private class StdStringAt extends StdStringTaintFunction {
|
||||
private class StdStringAt extends StdStringTaintFunction, SideEffectFunction, AliasFunction {
|
||||
StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -315,6 +387,22 @@ private class StdStringAt extends StdStringTaintFunction {
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,6 +412,54 @@ private class StdBasicIStream extends ClassTemplateInstantiation {
|
||||
StdBasicIStream() { this.hasQualifiedName(["std", "bsl"], "basic_istream") }
|
||||
}
|
||||
|
||||
private class StdBasicIfStream extends ClassTemplateInstantiation {
|
||||
StdBasicIfStream() { this.hasQualifiedName(["std", "bsl"], "basic_ifstream") }
|
||||
}
|
||||
|
||||
class StdBasicIfStreamConstructor extends Constructor, SideEffectFunction, AliasFunction {
|
||||
StdBasicIfStreamConstructor() { this.getDeclaringType() instanceof StdBasicIfStream }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
exists(Type t | t = this.getParameter(i).getUnspecifiedType() |
|
||||
t instanceof PointerType and buffer = true
|
||||
or
|
||||
t instanceof ReferenceType and buffer = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class StdBasicIfStreamDestructor extends Destructor, SideEffectFunction, AliasFunction {
|
||||
StdBasicIfStreamDestructor() { this.getDeclaringType() instanceof StdBasicIfStream }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = -1 and buffer = false and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::istream` function `operator>>` (defined as a member function).
|
||||
*/
|
||||
@@ -542,6 +678,33 @@ private class StdBasicOStream extends ClassTemplateInstantiation {
|
||||
StdBasicOStream() { this.hasQualifiedName(["std", "bsl"], "basic_ostream") }
|
||||
}
|
||||
|
||||
private class StdStringLessThan extends Function, AliasFunction, SideEffectFunction {
|
||||
StdStringLessThan() {
|
||||
this.hasQualifiedName(["std", "bsl"], "operator<") and
|
||||
this.getNumberOfParameters() = 2 and
|
||||
this.getParameter(0).getUnspecifiedType().(ReferenceType).getBaseType() instanceof
|
||||
StdBasicString and
|
||||
this.getParameter(1).getUnspecifiedType().(ReferenceType).getBaseType() instanceof
|
||||
StdBasicString
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = [0, 1] }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = [0, 1] and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::ostream` functions `operator<<` (defined as a member function),
|
||||
* `put` and `write`.
|
||||
|
||||
@@ -89,6 +89,14 @@ abstract class AllocationFunction extends Function {
|
||||
predicate requiresDealloc() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external allocation model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate allocationFunctionModel(
|
||||
string namespace, string type, boolean subtypes, string name, string sizeArg, string multArg,
|
||||
string reallocPtrArg, boolean requiresDealloc
|
||||
);
|
||||
|
||||
/**
|
||||
* An `operator new` or `operator new[]` function that may be associated with
|
||||
* `new` or `new[]` expressions. Note that `new` and `new[]` are not function
|
||||
|
||||
@@ -34,6 +34,13 @@ abstract class DeallocationFunction extends Function {
|
||||
int getFreedArg() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external deallocation model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate deallocationFunctionModel(
|
||||
string namespace, string type, boolean subtypes, string name, string freedArg
|
||||
);
|
||||
|
||||
/**
|
||||
* An `operator delete` or `operator delete[]` function that may be associated
|
||||
* with `delete` or `delete[]` expressions. Note that `delete` and `delete[]`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user