mirror of
https://github.com/github/codeql.git
synced 2026-05-26 09:01:22 +02:00
Compare commits
2 Commits
criemen/fi
...
redsun82/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd1f548fbc | ||
|
|
1dbe908024 |
1
.bazelrc
1
.bazelrc
@@ -24,6 +24,5 @@ common --registry=file:///%workspace%/misc/bazel/registry
|
||||
common --registry=https://bcr.bazel.build
|
||||
|
||||
common --@rules_dotnet//dotnet/settings:strict_deps=false
|
||||
common --experimental_isolated_extension_usages
|
||||
|
||||
try-import %workspace%/local.bazelrc
|
||||
|
||||
@@ -8,4 +8,3 @@ common --registry=https://bcr.bazel.build
|
||||
# its implementation packages without providing any code itself.
|
||||
# We either can depend on internal implementation details, or turn of strict deps.
|
||||
common --@rules_dotnet//dotnet/settings:strict_deps=false
|
||||
common --experimental_isolated_extension_usages
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
{
|
||||
"extensions": [
|
||||
"github.vscode-codeql",
|
||||
"hbenl.vscode-test-explorer",
|
||||
"ms-vscode.test-adapter-converter",
|
||||
"slevesque.vscode-zipexplorer",
|
||||
"ms-vscode.cpptools"
|
||||
],
|
||||
"settings": {
|
||||
"files.watcherExclude": {
|
||||
"**/target/**": true
|
||||
},
|
||||
"codeQL.runningQueries.memory": 2048
|
||||
},
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
},
|
||||
"runArgs": [
|
||||
"--cap-add=SYS_PTRACE",
|
||||
"--security-opt",
|
||||
"seccomp=unconfined"
|
||||
],
|
||||
"remoteUser": "vscode",
|
||||
"onCreateCommand": ".devcontainer/swift/user.sh"
|
||||
"extensions": [
|
||||
"github.vscode-codeql",
|
||||
"hbenl.vscode-test-explorer",
|
||||
"ms-vscode.test-adapter-converter",
|
||||
"slevesque.vscode-zipexplorer",
|
||||
"ms-vscode.cpptools"
|
||||
],
|
||||
"features": {
|
||||
"git-lfs": "latest"
|
||||
},
|
||||
"settings": {
|
||||
"files.watcherExclude": {
|
||||
"**/target/**": true
|
||||
},
|
||||
"codeQL.runningQueries.memory": 2048
|
||||
},
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
},
|
||||
"runArgs": [
|
||||
"--cap-add=SYS_PTRACE",
|
||||
"--security-opt",
|
||||
"seccomp=unconfined"
|
||||
],
|
||||
"remoteUser": "vscode",
|
||||
"onCreateCommand": ".devcontainer/swift/user.sh"
|
||||
}
|
||||
|
||||
@@ -3,9 +3,6 @@ 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 \
|
||||
@@ -21,7 +18,6 @@ apt-get -y install --no-install-recommends \
|
||||
python3-distutils \
|
||||
python3-pip \
|
||||
bash-completion \
|
||||
git-lfs \
|
||||
gh
|
||||
|
||||
# Install Bazel
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
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/buildifier.yml
vendored
2
.github/workflows/buildifier.yml
vendored
@@ -24,5 +24,5 @@ jobs:
|
||||
extra_args: >
|
||||
buildifier --all-files 2>&1 ||
|
||||
(
|
||||
echo -e "In order to format all bazel files, please run:\n bazel run //misc/bazel/buildifier"; exit 1
|
||||
echo -e "In order to format all bazel files, please run:\n bazel run //misc/bazel:buildifier"; exit 1
|
||||
)
|
||||
|
||||
5
.github/workflows/compile-queries.yml
vendored
5
.github/workflows/compile-queries.yml
vendored
@@ -29,6 +29,8 @@ jobs:
|
||||
key: all-queries
|
||||
- name: check formatting
|
||||
run: find shared */ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 -n 3000 -P 10 codeql query format -q --check-only
|
||||
- name: Omit DatabaseQualityDiagnostics.ql from compile checking # Remove me once CodeQL 2.18.0 is released!
|
||||
run: mv java/ql/src/Telemetry/DatabaseQualityDiagnostics.ql{,.hidden}
|
||||
- name: compile queries - check-only
|
||||
# run with --check-only if running in a PR (github.sha != main)
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
@@ -39,3 +41,6 @@ jobs:
|
||||
if : ${{ github.event_name != 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500
|
||||
- name: Restore DatabaseQualityDiagnostics.ql after compile checking # Remove me once CodeQL 2.18.0 is released
|
||||
run: mv java/ql/src/Telemetry/DatabaseQualityDiagnostics.ql{.hidden,}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ repos:
|
||||
name: Format bazel files
|
||||
files: \.(bazel|bzl)
|
||||
language: system
|
||||
entry: bazel run //misc/bazel/buildifier
|
||||
entry: bazel run //misc/bazel:buildifier
|
||||
pass_filenames: false
|
||||
|
||||
# DISABLED: can be enabled by copying this config and installing `pre-commit` with `--config` on the copy
|
||||
|
||||
36
MODULE.bazel
36
MODULE.bazel
@@ -1,7 +1,6 @@
|
||||
module(
|
||||
name = "ql",
|
||||
name = "codeql",
|
||||
version = "0.0",
|
||||
repo_name = "codeql",
|
||||
)
|
||||
|
||||
# this points to our internal repository when `codeql` is checked out as a submodule thereof
|
||||
@@ -15,7 +14,7 @@ local_path_override(
|
||||
# see https://registry.bazel.build/ for a list of available packages
|
||||
|
||||
bazel_dep(name = "platforms", version = "0.0.10")
|
||||
bazel_dep(name = "rules_go", version = "0.49.0")
|
||||
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.2.0-codeql.1")
|
||||
bazel_dep(name = "rules_python", version = "0.32.2")
|
||||
@@ -24,20 +23,18 @@ 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.38.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.49.1")
|
||||
bazel_dep(name = "rules_rust", version = "0.46.0")
|
||||
|
||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
|
||||
# crate_py but shortened due to Windows file path considerations
|
||||
cp = use_extension(
|
||||
crate = use_extension(
|
||||
"@rules_rust//crate_universe:extension.bzl",
|
||||
"crate",
|
||||
isolate = True,
|
||||
)
|
||||
cp.from_cargo(
|
||||
crate.from_cargo(
|
||||
name = "py_deps",
|
||||
cargo_lockfile = "//python/extractor/tsg-python:Cargo.lock",
|
||||
manifests = [
|
||||
@@ -45,23 +42,15 @@ cp.from_cargo(
|
||||
"//python/extractor/tsg-python/tsp:Cargo.toml",
|
||||
],
|
||||
)
|
||||
use_repo(cp, "py_deps")
|
||||
|
||||
# crate_ruby, but shortened due to windows file paths
|
||||
r = use_extension(
|
||||
"@rules_rust//crate_universe:extension.bzl",
|
||||
"crate",
|
||||
isolate = True,
|
||||
)
|
||||
r.from_cargo(
|
||||
name = "rd",
|
||||
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(r, ruby_deps = "rd")
|
||||
use_repo(crate, "py_deps", "ruby_deps")
|
||||
|
||||
dotnet = use_extension("@rules_dotnet//dotnet:extensions.bzl", "dotnet")
|
||||
dotnet.toolchain(dotnet_version = "8.0.101")
|
||||
@@ -123,7 +112,6 @@ 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",
|
||||
@@ -136,7 +124,6 @@ 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",
|
||||
@@ -149,16 +136,11 @@ 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(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
description: Revert support for using-enum declarations.
|
||||
compatibility: partial
|
||||
usings.rel: run usings.qlo
|
||||
using_container.rel: run using_container.qlo
|
||||
@@ -1,14 +0,0 @@
|
||||
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
|
||||
@@ -1,17 +0,0 @@
|
||||
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
|
||||
@@ -1,17 +0,0 @@
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
description: Add new builtin operations
|
||||
compatibility: partial
|
||||
exprs.rel: run exprs.qlo
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
description: description: Support explicit(bool) specifiers
|
||||
compatibility: full
|
||||
explicit_specifier_exprs.rel: delete
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Support destroying deletes
|
||||
compatibility: full
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
description: Add relation between deduction guides and class templates
|
||||
compatibility: full
|
||||
deduction_guide_for_class.rel: delete
|
||||
@@ -1,26 +1,3 @@
|
||||
## 1.4.0
|
||||
|
||||
### New Features
|
||||
|
||||
* A `getTemplateClass` predicate was added to the `DeductionGuide` class to get the class template for which the deduction guide is a guide.
|
||||
* An `isExplicit` predicate was added to the `Function` class that determines whether the function was declared as explicit.
|
||||
* A `getExplicitExpr` predicate was added to the `Function` class that yields the constant boolean expression (if any) that conditionally determines whether the function is explicit.
|
||||
* A `isDestroyingDeleteDeallocation` predicate was added to the `NewOrNewArrayExpr` and `DeleteOrDeleteArrayExpr` classes to indicate whether the deallocation function is a destroying delete.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The controlling expression of a `constexpr if` is now always recognized as an unevaluated expression.
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
## 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,14 +0,0 @@
|
||||
## 1.4.0
|
||||
|
||||
### New Features
|
||||
|
||||
* A `getTemplateClass` predicate was added to the `DeductionGuide` class to get the class template for which the deduction guide is a guide.
|
||||
* An `isExplicit` predicate was added to the `Function` class that determines whether the function was declared as explicit.
|
||||
* A `getExplicitExpr` predicate was added to the `Function` class that yields the constant boolean expression (if any) that conditionally determines whether the function is explicit.
|
||||
* A `isDestroyingDeleteDeallocation` predicate was added to the `NewOrNewArrayExpr` and `DeleteOrDeleteArrayExpr` classes to indicate whether the deallocation function is a destroying delete.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The controlling expression of a `constexpr if` is now always recognized as an unevaluated expression.
|
||||
* 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.
|
||||
* 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.0
|
||||
lastReleaseVersion: 1.2.0
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*1]", "ReturnValue", "taint", "manual"]
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*2]", "ReturnValue", "taint", "manual"]
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*3]", "ReturnValue", "taint", "manual"]
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*4]", "ReturnValue", "taint", "manual"]
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*5]", "ReturnValue", "taint", "manual"]
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*6]", "ReturnValue", "taint", "manual"]
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*7]", "ReturnValue", "taint", "manual"]
|
||||
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*8]", "ReturnValue", "taint", "manual"]
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 1.4.1-dev
|
||||
version: 1.2.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -158,26 +158,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
*/
|
||||
predicate isConsteval() { this.hasSpecifier("is_consteval") }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared to be `explicit`.
|
||||
*/
|
||||
predicate isExplicit() { this.hasSpecifier("explicit") }
|
||||
|
||||
/**
|
||||
* Gets the constant expression that determines whether the function is explicit.
|
||||
*
|
||||
* For example, for the following code the result is the expression `sizeof(T) == 1`:
|
||||
* ```
|
||||
* template<typename T> struct C {
|
||||
* explicit(sizeof(T) == 1)
|
||||
* C(const T);
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
Expr getExplicitExpr() {
|
||||
explicit_specifier_exprs(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function is declared with `__attribute__((naked))` or
|
||||
* `__declspec(naked)`.
|
||||
@@ -918,11 +898,4 @@ class UserDefinedLiteral extends Function {
|
||||
*/
|
||||
class DeductionGuide extends Function {
|
||||
DeductionGuide() { functions(underlyingElement(this), _, 8) }
|
||||
|
||||
/**
|
||||
* Gets the class template for which this is a deduction guide.
|
||||
*/
|
||||
TemplateClass getTemplateClass() {
|
||||
deduction_guide_for_class(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,13 +166,15 @@ class UsingEntry extends Locatable, @using {
|
||||
* ```
|
||||
*/
|
||||
class UsingDeclarationEntry extends UsingEntry {
|
||||
UsingDeclarationEntry() { usings(underlyingElement(this), _, _, 1) }
|
||||
UsingDeclarationEntry() {
|
||||
not exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() }
|
||||
}
|
||||
@@ -184,36 +186,19 @@ class UsingDeclarationEntry extends UsingEntry {
|
||||
* ```
|
||||
*/
|
||||
class UsingDirectiveEntry extends UsingEntry {
|
||||
UsingDirectiveEntry() { usings(underlyingElement(this), _, _, 2) }
|
||||
UsingDirectiveEntry() {
|
||||
exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()`
|
||||
|
||||
@@ -146,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 model
|
||||
string output, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
@@ -160,20 +160,16 @@ predicate sourceModel(
|
||||
row.splitAt(";", 6) = output and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
provenance = "manual" and
|
||||
model = ""
|
||||
provenance = "manual"
|
||||
or
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
Extensions::sourceModel(namespace, type, subtypes, name, signature, ext, output, kind,
|
||||
provenance, madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
Extensions::sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance,
|
||||
_)
|
||||
}
|
||||
|
||||
/** 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 model
|
||||
string input, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
@@ -187,14 +183,9 @@ predicate sinkModel(
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
provenance = "manual" and
|
||||
model = ""
|
||||
provenance = "manual"
|
||||
or
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
Extensions::sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance,
|
||||
madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
Extensions::sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +195,7 @@ predicate sinkModel(
|
||||
*/
|
||||
private predicate summaryModel0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance, string model
|
||||
string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
summaryModel(row) and
|
||||
@@ -219,14 +210,10 @@ private predicate summaryModel0(
|
||||
row.splitAt(";", 7) = output and
|
||||
row.splitAt(";", 8) = kind
|
||||
) and
|
||||
provenance = "manual" and
|
||||
model = ""
|
||||
provenance = "manual"
|
||||
or
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
Extensions::summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
Extensions::summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,20 +234,19 @@ private predicate expandInputAndOutput(
|
||||
*/
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance, string model
|
||||
string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(string input0, string output0 |
|
||||
summaryModel0(namespace, type, subtypes, name, signature, ext, input0, output0, kind,
|
||||
provenance, model) and
|
||||
summaryModel0(namespace, type, subtypes, name, signature, ext, input0, output0, kind, provenance) 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) {
|
||||
@@ -290,17 +276,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 model |
|
||||
string ext, string output, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance, model)
|
||||
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance)
|
||||
)
|
||||
or
|
||||
part = "sink" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string provenance, string model |
|
||||
string ext, string input, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance, model)
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
@@ -308,7 +294,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)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -317,9 +303,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
|
||||
@@ -336,9 +322,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
|
||||
@@ -348,11 +334,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>;
|
||||
@@ -393,11 +379,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."
|
||||
@@ -429,23 +415,18 @@ 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 getFullyTemplatedFunction(Function 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)
|
||||
)
|
||||
or
|
||||
not exists(f.getDeclaringType()) and
|
||||
f.isConstructedFrom(result)
|
||||
exists(Class c, Class templateClass, int i |
|
||||
c.isConstructedFrom(templateClass) and
|
||||
f = c.getAMember(i) and
|
||||
result = templateClass.getCanonicalMember(i)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -469,14 +450,14 @@ string getParameterTypeWithoutTemplateArguments(Function f, int n) {
|
||||
*/
|
||||
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
|
||||
exists(Function templateFunction |
|
||||
templateFunction = getFullyTemplatedFunction(f) and
|
||||
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 = getFullyTemplatedFunction(f) and
|
||||
templateFunction = getFullyTemplatedMemberFunction(f) and
|
||||
tp = templateFunction.getTemplateArgument(remaining) and
|
||||
result = mid.replaceAll(tp.getName(), "func:" + remaining.toString())
|
||||
)
|
||||
@@ -487,18 +468,12 @@ private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remain
|
||||
* with `class:N` (where `N` is the index of the template).
|
||||
*/
|
||||
private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining) {
|
||||
// If there is a declaring type then we start by expanding the function templates
|
||||
exists(Class template |
|
||||
f.getDeclaringType().isConstructedFrom(template) and
|
||||
remaining = template.getNumberOfTemplateArguments() and
|
||||
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
|
||||
)
|
||||
or
|
||||
// If there is no declaring type we're done after expanding the function templates
|
||||
not exists(f.getDeclaringType()) and
|
||||
remaining = 0 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
|
||||
@@ -581,6 +556,38 @@ private string getSignatureWithoutFunctionTemplateNames(
|
||||
)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function c, int i) {
|
||||
not c.isFromUninstantiatedTemplate(_) and
|
||||
(
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
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 = ")"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a parenthesized string containing all parameter types of this callable, separated by a comma.
|
||||
*
|
||||
* Returns the empty string if the callable has no parameters.
|
||||
* Parameter types are represented by their type erasure.
|
||||
*/
|
||||
cached
|
||||
private string paramsString(Function c) {
|
||||
result = concat(int i | | paramsStringPart(c, i) order by i)
|
||||
}
|
||||
|
||||
bindingset[func]
|
||||
private predicate matchesSignature(Function func, string signature) {
|
||||
signature = "" or
|
||||
paramsString(func) = signature
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(_, type, _, name, signature, _)` holds and
|
||||
* - `typeArgs` represents the named template parameters supplied to `type`, and
|
||||
@@ -729,17 +736,17 @@ private predicate elementSpecWithArguments0(
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(namespace, type, subtypes, name, signature, _)` and
|
||||
* `func`'s signature matches `signature`.
|
||||
* `method`'s signature matches `signature`.
|
||||
*
|
||||
* `signature` may contain template parameter names that are bound by `type` and `name`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate elementSpecMatchesSignature(
|
||||
Function func, string namespace, string type, boolean subtypes, string name, string signature
|
||||
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(func, signature, type, name, 0)
|
||||
signatureMatches(method, signature, type, name, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,22 +762,13 @@ private predicate hasClassAndName(Class classWithMethod, Function method, string
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[name]
|
||||
pragma[inline_late]
|
||||
private predicate funcHasQualifiedName(Function func, string namespace, string name) {
|
||||
exists(string nameWithoutArgs |
|
||||
parseAngles(name, nameWithoutArgs, _, "") and
|
||||
func.hasQualifiedName(namespace, nameWithoutArgs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `namedClass` is in namespace `namespace` and has
|
||||
* name `type` (excluding any template parameters).
|
||||
*/
|
||||
bindingset[type, namespace]
|
||||
pragma[inline_late]
|
||||
private predicate classHasQualifiedName(Class namedClass, string namespace, string type) {
|
||||
private predicate hasQualifiedName(Class namedClass, string namespace, string type) {
|
||||
exists(string typeWithoutArgs |
|
||||
parseAngles(type, typeWithoutArgs, _, "") and
|
||||
namedClass.hasQualifiedName(namespace, typeWithoutArgs)
|
||||
@@ -792,16 +790,15 @@ private Element interpretElement0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
(
|
||||
// Non-member functions
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
subtypes = false and
|
||||
type = "" and
|
||||
(
|
||||
elementSpecMatchesSignature(result, namespace, type, subtypes, name, signature)
|
||||
or
|
||||
signature = "" and
|
||||
elementSpec(namespace, type, subtypes, name, "", _) and
|
||||
funcHasQualifiedName(result, namespace, name)
|
||||
// Non-member functions
|
||||
exists(Function func |
|
||||
func.hasQualifiedName(namespace, name) and
|
||||
type = "" and
|
||||
matchesSignature(func, signature) and
|
||||
subtypes = false and
|
||||
not exists(func.getDeclaringType()) and
|
||||
result = func
|
||||
)
|
||||
or
|
||||
// Member functions
|
||||
@@ -814,7 +811,7 @@ private Element interpretElement0(
|
||||
elementSpec(namespace, type, subtypes, name, "", _) and
|
||||
hasClassAndName(classWithMethod, result, name)
|
||||
) and
|
||||
classHasQualifiedName(namedClass, namespace, type) and
|
||||
hasQualifiedName(namedClass, namespace, type) and
|
||||
(
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
@@ -870,9 +867,9 @@ private module Cached {
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(DataFlow::Node node, string kind, string model) {
|
||||
predicate sourceNode(DataFlow::Node node, string kind) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSourceNode(n, kind, model) and n.asNode() = node
|
||||
isSourceNode(n, kind, _) and n.asNode() = node // TODO
|
||||
)
|
||||
}
|
||||
|
||||
@@ -881,57 +878,40 @@ private module Cached {
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(DataFlow::Node node, string kind, string model) {
|
||||
predicate sinkNode(DataFlow::Node node, string kind) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSinkNode(n, kind, model) and n.asNode() = node
|
||||
isSinkNode(n, kind, _) and n.asNode() = node // TODO
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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, string model
|
||||
Function f, string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance,
|
||||
model) and
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) 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, string model
|
||||
) {
|
||||
private predicate relevantSummaryElementManual(string input, string output, string kind) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance, model) and
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
provenance.isManual()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElementGenerated(
|
||||
string input, string output, string kind, string model
|
||||
) {
|
||||
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance, model) and
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
provenance.isGenerated()
|
||||
)
|
||||
}
|
||||
@@ -940,16 +920,35 @@ private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
string input, string output, boolean preservesValue, string model
|
||||
) {
|
||||
exists(string kind |
|
||||
this.relevantSummaryElementManual(input, output, kind, model)
|
||||
this.relevantSummaryElementManual(input, output, kind)
|
||||
or
|
||||
not this.relevantSummaryElementManual(_, _, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind, model)
|
||||
not this.relevantSummaryElementManual(_, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind)
|
||||
|
|
||||
if kind = "value" then preservesValue = true else preservesValue = false
|
||||
)
|
||||
) and
|
||||
model = "" // TODO
|
||||
}
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) {
|
||||
interpretSummary(this, _, _, _, provenance, _)
|
||||
interpretSummary(this, _, _, _, provenance)
|
||||
}
|
||||
}
|
||||
|
||||
// adapter class for converting Mad neutrals to `NeutralCallable`s
|
||||
private class NeutralCallableAdapter extends NeutralCallable {
|
||||
string kind;
|
||||
string provenance_;
|
||||
|
||||
NeutralCallableAdapter() {
|
||||
// Neutral models have not been implemented for CPP.
|
||||
none() and
|
||||
exists(this) and
|
||||
exists(kind) and
|
||||
exists(provenance_)
|
||||
}
|
||||
|
||||
override string getKind() { result = kind }
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
|
||||
}
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -215,16 +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. */
|
||||
DataFlowType getNodeType(Node n) {
|
||||
Type 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`.
|
||||
*/
|
||||
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
|
||||
predicate compatibleTypes(Type t1, Type t2) {
|
||||
t1 instanceof VoidType and t2 instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
@@ -236,15 +239,21 @@ class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
class DataFlowCallable extends Function { }
|
||||
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 DataFlowExpr = Expr;
|
||||
|
||||
final private class TypeFinal = Type;
|
||||
|
||||
class DataFlowType extends TypeFinal {
|
||||
string toString() { result = "" }
|
||||
}
|
||||
class DataFlowType = Type;
|
||||
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends Expr instanceof Call {
|
||||
@@ -260,12 +269,24 @@ 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
|
||||
|
||||
@@ -112,8 +112,9 @@ 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, model) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext) and
|
||||
model = "" // TODO
|
||||
)
|
||||
}
|
||||
|
||||
@@ -127,8 +128,9 @@ 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, model) and
|
||||
e = interpretElement(package, type, subtypes, name, signature, ext)
|
||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
|
||||
e = interpretElement(package, type, subtypes, name, signature, ext) and
|
||||
model = "" // TODO
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -383,37 +383,6 @@ 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).
|
||||
@@ -678,7 +647,8 @@ 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 an assignment operator with an empty exception specification.
|
||||
* Returns true if there exists a `C::operator =(const D& d) nothrow`
|
||||
* assignment operator (i.e, with an empty exception specification).
|
||||
* ```
|
||||
* bool v = __is_nothrow_assignable(MyType1, MyType2);
|
||||
* ```
|
||||
@@ -693,7 +663,8 @@ 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 an assignment operator.
|
||||
* Returns true if there exists a `C::operator =(const D& d)` assignment
|
||||
* operator.
|
||||
* ```
|
||||
* bool v = __is_assignable(MyType1, MyType2);
|
||||
* ```
|
||||
@@ -704,25 +675,6 @@ 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).
|
||||
@@ -756,20 +708,6 @@ 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).
|
||||
@@ -1124,24 +1062,6 @@ 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).
|
||||
@@ -1200,87 +1120,6 @@ 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).
|
||||
@@ -1299,42 +1138,6 @@ 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).
|
||||
@@ -1751,10 +1554,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 {
|
||||
@@ -1762,126 +1565,3 @@ 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" }
|
||||
}
|
||||
|
||||
@@ -307,10 +307,6 @@ class Expr extends StmtParent, @expr {
|
||||
)
|
||||
or
|
||||
exists(Decltype d | d.getExpr() = this.getParentWithConversions*())
|
||||
or
|
||||
exists(ConstexprIfStmt constIf |
|
||||
constIf.getControllingExpr() = this.getParentWithConversions*()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -859,16 +855,6 @@ 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.
|
||||
*
|
||||
@@ -963,16 +949,6 @@ 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;
|
||||
@@ -1039,16 +1015,6 @@ 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.
|
||||
*/
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,14 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ 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 {
|
||||
@@ -994,6 +993,9 @@ DataFlowType getNodeType(Node n) {
|
||||
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`.
|
||||
@@ -1058,6 +1060,16 @@ 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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1094,11 +1106,7 @@ class SummarizedCallable extends DataFlowCallable, TSummarizedCallable {
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
final private class TypeFinal = Type;
|
||||
|
||||
class DataFlowType extends TypeFinal {
|
||||
string toString() { result = "" }
|
||||
}
|
||||
class DataFlowType = Type;
|
||||
|
||||
cached
|
||||
private newtype TDataFlowCall =
|
||||
@@ -1159,6 +1167,16 @@ 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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1251,6 +1269,15 @@ 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) {
|
||||
@@ -1335,9 +1362,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) { External::sourceNode(source, _, model) }
|
||||
predicate knownSourceModel(Node source, string model) { none() }
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { External::sinkNode(sink, _, model) }
|
||||
predicate knownSinkModel(Node sink, string model) { none() }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
|
||||
@@ -104,7 +104,7 @@ predicate hasRawIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
|
||||
cached
|
||||
private newtype TDefImpl =
|
||||
TDefAddressImpl(BaseSourceVariable v) or
|
||||
TDefAddressImpl(BaseIRVariable 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 SSA variable address. */
|
||||
abstract private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||
BaseSourceVariable v;
|
||||
/** An initial definition of an `IRVariable`'s address. */
|
||||
private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||
BaseIRVariable v;
|
||||
|
||||
DefAddressImpl() {
|
||||
this = TDefAddressImpl(v) and
|
||||
@@ -342,19 +342,6 @@ abstract 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)
|
||||
@@ -366,14 +353,15 @@ private class DefVariableAddressImpl extends DefAddressImpl {
|
||||
index = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class DefCallAddressImpl extends DefAddressImpl {
|
||||
override BaseCallVariable v;
|
||||
override Cpp::Location getLocation() { result = v.getIRVariable().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
block.getInstruction(index) = v.getCallInstruction()
|
||||
final override SourceVariable getSourceVariable() {
|
||||
result.getBaseVariable() = v and
|
||||
result.getIndirection() = 0
|
||||
}
|
||||
|
||||
final override BaseSourceVariable getBaseSourceVariable() { result = v }
|
||||
}
|
||||
|
||||
private class DirectDef extends DefImpl, TDirectDefImpl {
|
||||
|
||||
@@ -40,8 +40,7 @@ predicate ignoreInstruction(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction or
|
||||
instr instanceof AliasedUseInstruction or
|
||||
instr instanceof InitializeNonLocalInstruction or
|
||||
instr instanceof ReturnIndirectionInstruction or
|
||||
instr instanceof UninitializedGroupInstruction
|
||||
instr instanceof ReturnIndirectionInstruction
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ private newtype TMemoryAccessKind =
|
||||
TPhiMemoryAccess() or
|
||||
TUnmodeledMemoryAccess() or
|
||||
TChiTotalMemoryAccess() or
|
||||
TChiPartialMemoryAccess() or
|
||||
TGroupedMemoryAccess()
|
||||
TChiPartialMemoryAccess()
|
||||
|
||||
/**
|
||||
* Describes the set of memory locations memory accessed by a memory operand or
|
||||
@@ -100,11 +99,3 @@ 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,7 +89,6 @@ private newtype TOpcode =
|
||||
TSizedBufferMayWriteSideEffect() or
|
||||
TInitializeDynamicAllocation() or
|
||||
TChi() or
|
||||
TUninitializedGroup() or
|
||||
TInlineAsm() or
|
||||
TUnreached() or
|
||||
TNewObj()
|
||||
@@ -1238,17 +1237,6 @@ 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,47 +2142,6 @@ 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,7 +106,8 @@ private predicate operandEscapesDomain(Operand operand) {
|
||||
not isArgumentForParameter(_, operand, _) and
|
||||
not isOnlyEscapesViaReturnArgument(operand) and
|
||||
not operand.getUse() instanceof ReturnValueInstruction and
|
||||
not operand.getUse() instanceof ReturnIndirectionInstruction
|
||||
not operand.getUse() instanceof ReturnIndirectionInstruction and
|
||||
not operand instanceof PhiInputOperand
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,11 +191,6 @@ 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) {
|
||||
@@ -216,6 +212,9 @@ private predicate operandEscapesNonReturn(Operand operand) {
|
||||
or
|
||||
isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUse())
|
||||
or
|
||||
operand instanceof PhiInputOperand and
|
||||
resultEscapesNonReturn(operand.getUse())
|
||||
or
|
||||
operandEscapesDomain(operand)
|
||||
}
|
||||
|
||||
@@ -237,6 +236,9 @@ 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) {
|
||||
@@ -338,56 +340,6 @@ 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
|
||||
@@ -396,14 +348,12 @@ private predicate operandConsumesEscaped(Configuration::Allocation allocation) {
|
||||
predicate allocationEscapes(Configuration::Allocation allocation) {
|
||||
allocation.alwaysEscapes()
|
||||
or
|
||||
exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() |
|
||||
resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
or
|
||||
operandConsumesEscaped(allocation)
|
||||
exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
)
|
||||
or
|
||||
Configuration::phaseNeedsSoundEscapeAnalysis() and
|
||||
(resultEscapesNonReturn(allocation.getABaseInstruction()) or operandConsumesEscaped(allocation))
|
||||
resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,8 +101,6 @@ 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 {
|
||||
@@ -146,8 +144,3 @@ class DynamicAllocation extends Allocation, TDynamicAllocation {
|
||||
}
|
||||
|
||||
predicate phaseNeedsSoundEscapeAnalysis() { none() }
|
||||
|
||||
UnaliasedSsa::Allocation getOldAllocation(VariableAllocation allocation) {
|
||||
UnaliasedSsa::canReuseSsaForVariable(allocation.getIRVariable()) and
|
||||
result = allocation.getIRVariable()
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ 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
|
||||
import AliasConfiguration
|
||||
private import codeql.util.Boolean
|
||||
private import AliasConfiguration
|
||||
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
@@ -17,196 +16,49 @@ private predicate isIndirectOrBufferMemoryAccess(MemoryAccessKind kind) {
|
||||
kind instanceof BufferMemoryAccess
|
||||
}
|
||||
|
||||
private predicate hasMemoryAccess(
|
||||
AddressOperand addrOperand, Allocation var, IntValue startBitOffset, boolean grouped
|
||||
) {
|
||||
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
|
||||
Instruction instr, Allocation var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess
|
||||
) {
|
||||
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()
|
||||
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()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasOperandMemoryAccess(
|
||||
AddressOperand address, MemoryOperand operand, Allocation var, IRType type,
|
||||
Language::LanguageType languageType, IntValue startBitOffset, IntValue endBitOffset,
|
||||
boolean isMayAccess, boolean grouped
|
||||
MemoryOperand operand, Allocation var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess
|
||||
) {
|
||||
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)
|
||||
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()
|
||||
)
|
||||
}
|
||||
|
||||
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, false)
|
||||
hasResultMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
or
|
||||
hasOperandMemoryAccess(_, _, var, type, _, startBitOffset, endBitOffset, isMayAccess, false)
|
||||
hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
or
|
||||
// For a stack variable, always create a memory location for the entire variable.
|
||||
var.isAlwaysAllocatedOnStack() and
|
||||
@@ -217,25 +69,31 @@ private newtype TMemoryLocation =
|
||||
) and
|
||||
languageType = type.getCanonicalLanguageType()
|
||||
} or
|
||||
TEntireAllocationMemoryLocation(Allocation var, Boolean isMayAccess) {
|
||||
var instanceof IndirectParameterAllocation or
|
||||
var instanceof DynamicAllocation
|
||||
TEntireAllocationMemoryLocation(Allocation var, boolean isMayAccess) {
|
||||
(
|
||||
var instanceof IndirectParameterAllocation or
|
||||
var instanceof DynamicAllocation
|
||||
) and
|
||||
(isMayAccess = false or isMayAccess = true)
|
||||
} or
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* A memory location accessed by a memory operand or memory result. In this implementation, the location is
|
||||
* Represents the 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 private class MemoryLocation0 extends TMemoryLocation {
|
||||
abstract class MemoryLocation extends TMemoryLocation {
|
||||
final string toString() {
|
||||
if this.isMayAccess()
|
||||
then result = "?" + this.toStringInternal()
|
||||
@@ -258,14 +116,7 @@ abstract private class MemoryLocation0 extends TMemoryLocation {
|
||||
|
||||
abstract predicate isMayAccess();
|
||||
|
||||
/**
|
||||
* 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() }
|
||||
Allocation getAllocation() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the location cannot be overwritten except by definition of a `MemoryLocation` for
|
||||
@@ -296,35 +147,30 @@ abstract private class MemoryLocation0 extends TMemoryLocation {
|
||||
* represented by a `MemoryLocation` that totally overlaps all other
|
||||
* `MemoryLocations` in the set.
|
||||
*/
|
||||
abstract class VirtualVariable extends MemoryLocation0 { }
|
||||
abstract class VirtualVariable extends MemoryLocation { }
|
||||
|
||||
abstract class AllocationMemoryLocation extends MemoryLocation0 {
|
||||
abstract class AllocationMemoryLocation extends MemoryLocation {
|
||||
Allocation var;
|
||||
boolean isMayAccess;
|
||||
|
||||
bindingset[isMayAccess]
|
||||
AllocationMemoryLocation() { any() }
|
||||
AllocationMemoryLocation() {
|
||||
this instanceof TMemoryLocation and
|
||||
isMayAccess = false
|
||||
or
|
||||
isMayAccess = true // Just ensures that `isMayAccess` is bound.
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
if allocationEscapes(var)
|
||||
then result = TAllAliasedMemory(var.getEnclosingIRFunction(), false)
|
||||
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
|
||||
)
|
||||
else result.(AllocationMemoryLocation).getAllocation() = var
|
||||
}
|
||||
|
||||
final override IRFunction getIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final override Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final override Allocation getAnAllocation() { result = var }
|
||||
final override Allocation getAllocation() { result = var }
|
||||
|
||||
final override predicate isMayAccess() { isMayAccess = true }
|
||||
|
||||
@@ -365,13 +211,13 @@ class VariableMemoryLocation extends TVariableMemoryLocation, AllocationMemoryLo
|
||||
final override Language::LanguageType getType() {
|
||||
if
|
||||
strictcount(Language::LanguageType accessType |
|
||||
hasResultMemoryAccess(_, _, var, type, accessType, startBitOffset, endBitOffset, _, false) or
|
||||
hasOperandMemoryAccess(_, _, var, type, accessType, startBitOffset, endBitOffset, _, false)
|
||||
hasResultMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset, _) or
|
||||
hasOperandMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset, _)
|
||||
) = 1
|
||||
then
|
||||
// All of the accesses have the same `LanguageType`, so just use that.
|
||||
hasResultMemoryAccess(_, _, var, type, result, startBitOffset, endBitOffset, _, false) or
|
||||
hasOperandMemoryAccess(_, _, var, type, result, startBitOffset, endBitOffset, _, false)
|
||||
hasResultMemoryAccess(_, var, type, result, startBitOffset, endBitOffset, _) or
|
||||
hasOperandMemoryAccess(_, var, type, result, startBitOffset, endBitOffset, _)
|
||||
else
|
||||
// There is no single type for all accesses, so just use the canonical one for this `IRType`.
|
||||
result = type.getCanonicalLanguageType()
|
||||
@@ -401,89 +247,6 @@ 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
|
||||
{
|
||||
@@ -519,18 +282,10 @@ 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, MemoryLocation0 {
|
||||
class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
|
||||
IRFunction irFunc;
|
||||
boolean isMayAccess;
|
||||
|
||||
@@ -557,7 +312,7 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation0 {
|
||||
* 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, MemoryLocation0 {
|
||||
class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
|
||||
IRFunction irFunc;
|
||||
boolean isMayAccess;
|
||||
|
||||
@@ -591,7 +346,7 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation0 {
|
||||
/**
|
||||
* An access to all aliased memory.
|
||||
*/
|
||||
class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation0 {
|
||||
class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
|
||||
IRFunction irFunc;
|
||||
boolean isMayAccess;
|
||||
|
||||
@@ -622,7 +377,7 @@ class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
|
||||
/**
|
||||
* Gets the overlap relationship between the definition location `def` and the use location `use`.
|
||||
*/
|
||||
Overlap getOverlap(MemoryLocation0 def, MemoryLocation0 use) {
|
||||
Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
exists(Overlap overlap |
|
||||
// Compute the overlap based only on the extent.
|
||||
overlap = getExtentOverlap(def, use) and
|
||||
@@ -650,7 +405,7 @@ Overlap getOverlap(MemoryLocation0 def, MemoryLocation0 use) {
|
||||
* based only on the set of memory locations accessed. Handling of "may" accesses and read-only
|
||||
* locations occurs in `getOverlap()`.
|
||||
*/
|
||||
private Overlap getExtentOverlap(MemoryLocation0 def, MemoryLocation0 use) {
|
||||
private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation 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.
|
||||
@@ -691,7 +446,7 @@ private Overlap getExtentOverlap(MemoryLocation0 def, MemoryLocation0 use) {
|
||||
result instanceof MustExactlyOverlap
|
||||
or
|
||||
not use instanceof EntireAllocationMemoryLocation and
|
||||
if def.getAnAllocation() = use.getAnAllocation()
|
||||
if def.getAllocation() = use.getAllocation()
|
||||
then
|
||||
// EntireAllocationMemoryLocation totally overlaps any location within
|
||||
// the same allocation.
|
||||
@@ -699,48 +454,11 @@ private Overlap getExtentOverlap(MemoryLocation0 def, MemoryLocation0 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.getAnAllocation()) and
|
||||
not exists(use.getAllocation()) 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
|
||||
(
|
||||
@@ -750,8 +468,7 @@ private Overlap getExtentOverlap(MemoryLocation0 def, MemoryLocation0 use) {
|
||||
(
|
||||
use instanceof UnknownMemoryLocation or
|
||||
use instanceof AllAliasedMemory or
|
||||
use instanceof EntireAllocationMemoryLocation or
|
||||
use instanceof GroupedMemoryLocation
|
||||
use instanceof EntireAllocationMemoryLocation
|
||||
) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
@@ -817,7 +534,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.getAnAllocation() and
|
||||
var = vml.getAllocation() and
|
||||
vvar = vml.getVirtualVariable() and
|
||||
isRelatableMemoryLocation(vml) and
|
||||
offsetRank in [startRank .. endRank]
|
||||
@@ -825,7 +542,7 @@ private predicate isCoveredOffset(Allocation var, int offsetRank, VariableMemory
|
||||
}
|
||||
|
||||
private predicate hasUnknownOffset(Allocation var, VariableMemoryLocation vml) {
|
||||
vml.getAnAllocation() = var and
|
||||
vml.getAllocation() = var and
|
||||
(
|
||||
vml.getStartBitOffset() = Ints::unknown() or
|
||||
vml.getEndBitOffset() = Ints::unknown()
|
||||
@@ -840,9 +557,9 @@ private predicate overlappingIRVariableMemoryLocations(
|
||||
isCoveredOffset(var, offsetRank, use)
|
||||
)
|
||||
or
|
||||
hasUnknownOffset(use.getAnAllocation(), def)
|
||||
hasUnknownOffset(use.getAllocation(), def)
|
||||
or
|
||||
hasUnknownOffset(def.getAnAllocation(), use)
|
||||
hasUnknownOffset(def.getAllocation(), use)
|
||||
}
|
||||
|
||||
private Overlap getVariableMemoryLocationOverlap(
|
||||
@@ -863,40 +580,6 @@ 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 |
|
||||
@@ -905,24 +588,13 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
(
|
||||
(
|
||||
isIndirectOrBufferMemoryAccess(kind) and
|
||||
if hasResultMemoryAccess(_, instr, _, _, _, _, _, _, _)
|
||||
if hasResultMemoryAccess(instr, _, _, _, _, _, _)
|
||||
then
|
||||
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)
|
||||
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))
|
||||
)
|
||||
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
@@ -941,31 +613,20 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
)
|
||||
}
|
||||
|
||||
private MemoryLocation0 getOperandMemoryLocation0(MemoryOperand operand, boolean isMayAccess) {
|
||||
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
not canReuseSsaForOldResult(operand.getAnyDef()) and
|
||||
exists(MemoryAccessKind kind |
|
||||
exists(MemoryAccessKind kind, boolean isMayAccess |
|
||||
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,
|
||||
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)
|
||||
exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, _, startBitOffset, endBitOffset, isMayAccess) and
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
)
|
||||
else result = TUnknownMemoryLocation(operand.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
@@ -984,19 +645,6 @@ private MemoryLocation0 getOperandMemoryLocation0(MemoryOperand operand, boolean
|
||||
)
|
||||
}
|
||||
|
||||
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,51 +15,6 @@ 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
|
||||
@@ -77,11 +32,6 @@ private module Cached {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiNodeAfterUninitializedGroup(UninitializedGroupInstruction initGroup) {
|
||||
hasChiNodeAfterUninitializedGroup(_, initGroup)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
@@ -95,8 +45,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction or
|
||||
TUninitializedGroupInstruction;
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
/**
|
||||
* If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block,
|
||||
@@ -129,8 +78,6 @@ private module Cached {
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUninitializedGroupInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
@@ -176,8 +123,7 @@ private module Cached {
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
canModelResultForOldInstruction(getOldInstruction(instruction)) or
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction or // Chis always have modeled results
|
||||
instruction instanceof UninitializedGroupInstruction // Group initializers always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -188,23 +134,16 @@ 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(Instruction input | instruction = getChi(input) |
|
||||
Alias::getResultMemoryLocation(input).getVirtualVariable() instanceof
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).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.getAnAllocation())
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -266,11 +205,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
(
|
||||
instruction = getChi(getOldInstruction(result))
|
||||
or
|
||||
instruction = getChi(result.(UninitializedGroupInstruction))
|
||||
) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -328,14 +263,6 @@ 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.
|
||||
@@ -389,19 +316,6 @@ 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(
|
||||
@@ -415,19 +329,6 @@ 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
|
||||
@@ -443,40 +344,14 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private UninitializedGroupInstruction firstInstructionToUninitializedGroup(
|
||||
Instruction instruction, EdgeKind kind
|
||||
) {
|
||||
exists(IRFunction func |
|
||||
isFirstInstructionBeforeUninitializedGroup(instruction, func) and
|
||||
result = getInitGroupInstruction(0, func) and
|
||||
kind instanceof GotoEdge
|
||||
)
|
||||
}
|
||||
/*
|
||||
* 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 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
|
||||
) {
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
@@ -496,107 +371,6 @@ 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 |
|
||||
@@ -632,16 +406,6 @@ 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
|
||||
@@ -654,16 +418,9 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
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()
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
instr = reusedPhiInstruction(_) and
|
||||
@@ -691,8 +448,6 @@ 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
|
||||
}
|
||||
|
||||
@@ -705,15 +460,10 @@ private module Cached {
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
exists(Instruction primaryInstr |
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
exists(Alias::VariableGroup vg |
|
||||
instr = uninitializedGroup(vg) and
|
||||
result = vg.getIRFunction()
|
||||
)
|
||||
or
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
@@ -728,8 +478,6 @@ private module Cached {
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
or
|
||||
instruction = getChi(result.(UninitializedGroupInstruction))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,7 +485,7 @@ private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(Instruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
@@ -758,16 +506,6 @@ 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
|
||||
|
||||
/**
|
||||
@@ -930,37 +668,19 @@ 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`.
|
||||
*/
|
||||
private Instruction getDefinitionOrChiInstruction0(
|
||||
Instruction getDefinitionOrChiInstruction(
|
||||
OldBlock defBlock, int defOffset, Alias::MemoryLocation defLocation,
|
||||
Alias::MemoryLocation actualDefLocation
|
||||
) {
|
||||
exists(OldInstruction oldInstr, int oldOffset | oldInstr = defBlock.getInstruction(oldOffset) |
|
||||
exists(OldInstruction oldInstr, int oldOffset |
|
||||
oldInstr = defBlock.getInstruction(oldOffset) and
|
||||
oldOffset >= 0
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = getChiOffset(oldOffset, defBlock) and
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
@@ -969,7 +689,7 @@ module DefUse {
|
||||
actualDefLocation = defLocation.getVirtualVariable()
|
||||
or
|
||||
// An even offset corresponds to the original instruction.
|
||||
defOffset = getNonChiOffset(oldOffset, defBlock) and
|
||||
defOffset = oldOffset * 2 and
|
||||
result = getNewInstruction(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
@@ -982,54 +702,6 @@ 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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1170,20 +842,8 @@ module DefUse {
|
||||
block.getInstruction(index) = def and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation) and
|
||||
if overlap instanceof MayPartiallyOverlap
|
||||
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.
|
||||
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.
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1244,11 +904,10 @@ module DefUse {
|
||||
block.getInstruction(index) = use and
|
||||
(
|
||||
// A direct use of the location.
|
||||
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and
|
||||
offset = getNonChiOffset(index, block)
|
||||
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and offset = index * 2
|
||||
or
|
||||
// A `Chi` instruction will include a use of the virtual variable.
|
||||
hasChiNode(useLocation, use) and offset = getChiOffset(index, block)
|
||||
hasChiNode(useLocation, use) and offset = (index * 2) + 1
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1398,9 +1057,5 @@ module Ssa {
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasChiNodeAfterUninitializedGroup = Cached::hasChiNodeAfterUninitializedGroup/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
|
||||
class VariableGroup = Alias::VariableGroup;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ newtype TInstruction =
|
||||
TUnaliasedSsaUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
UnaliasedSsa::Ssa::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TUnaliasedSsaUninitializedGroupInstruction(UnaliasedSsa::Ssa::VariableGroup vg) or
|
||||
TAliasedSsaPhiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSsa::Ssa::MemoryLocation memoryLocation
|
||||
) {
|
||||
@@ -42,12 +41,6 @@ newtype TInstruction =
|
||||
} or
|
||||
TAliasedSsaUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
AliasedSsa::Ssa::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TAliasedSsaUninitializedGroupInstruction(AliasedSsa::Ssa::VariableGroup vg) or
|
||||
TAliasedSsaChiAfterUninitializedGroupInstruction(
|
||||
TAliasedSsaUninitializedGroupInstruction initGroup
|
||||
) {
|
||||
AliasedSsa::Ssa::hasChiNodeAfterUninitializedGroup(initGroup)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,11 +62,7 @@ module UnaliasedSsaInstructions {
|
||||
|
||||
class TChiInstruction = TUnaliasedSsaChiInstruction;
|
||||
|
||||
class TUninitializedGroupInstruction = TUnaliasedSsaUninitializedGroupInstruction;
|
||||
|
||||
class TRawOrUninitializedGroupInstruction = TRawInstruction or TUninitializedGroupInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawOrUninitializedGroupInstruction primaryInstruction) {
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TUnaliasedSsaChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
@@ -82,12 +71,6 @@ 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() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,16 +92,10 @@ module AliasedSsaInstructions {
|
||||
result = TUnaliasedSsaPhiInstruction(blockStartInstr, _)
|
||||
}
|
||||
|
||||
class TChiInstruction =
|
||||
TAliasedSsaChiInstruction or TAliasedSsaChiAfterUninitializedGroupInstruction;
|
||||
class TChiInstruction = TAliasedSsaChiInstruction;
|
||||
|
||||
class TRawOrInitialzieGroupInstruction =
|
||||
TRawInstruction or TAliasedSsaUninitializedGroupInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawOrInitialzieGroupInstruction primaryInstruction) {
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TAliasedSsaChiInstruction(primaryInstruction)
|
||||
or
|
||||
result = TAliasedSsaChiAfterUninitializedGroupInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TAliasedSsaUnreachedInstruction;
|
||||
@@ -126,12 +103,4 @@ 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,9 +12,6 @@ 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`),
|
||||
@@ -55,7 +52,7 @@ private module Internal {
|
||||
) {
|
||||
exists(AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap))
|
||||
} or
|
||||
TAliasedChiOperand(TAliasedChiInstruction useInstr, ChiOperandTag tag) { any() }
|
||||
TAliasedChiOperand(TAliasedSsaChiInstruction useInstr, ChiOperandTag tag) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,13 +198,10 @@ module AliasedSsaOperands {
|
||||
)
|
||||
}
|
||||
|
||||
private class TChiInstruction =
|
||||
TAliasedSsaChiInstruction or TAliasedSsaChiAfterUninitializedGroupInstruction;
|
||||
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
TChiOperand chiOperand(TChiInstruction useInstr, ChiOperandTag tag) {
|
||||
TChiOperand chiOperand(TAliasedSsaChiInstruction useInstr, ChiOperandTag tag) {
|
||||
result = Internal::TAliasedChiOperand(useInstr, tag)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2142,47 +2142,6 @@ 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.
|
||||
*
|
||||
|
||||
@@ -407,8 +407,6 @@ predicate hasUnreachedInstruction(IRFunction func) {
|
||||
)
|
||||
}
|
||||
|
||||
IRVariable getAnUninitializedGroupVariable(UninitializedGroupInstruction instr) { none() }
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
cached
|
||||
|
||||
@@ -2142,47 +2142,6 @@ 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,7 +106,8 @@ private predicate operandEscapesDomain(Operand operand) {
|
||||
not isArgumentForParameter(_, operand, _) and
|
||||
not isOnlyEscapesViaReturnArgument(operand) and
|
||||
not operand.getUse() instanceof ReturnValueInstruction and
|
||||
not operand.getUse() instanceof ReturnIndirectionInstruction
|
||||
not operand.getUse() instanceof ReturnIndirectionInstruction and
|
||||
not operand instanceof PhiInputOperand
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,11 +191,6 @@ 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) {
|
||||
@@ -216,6 +212,9 @@ private predicate operandEscapesNonReturn(Operand operand) {
|
||||
or
|
||||
isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUse())
|
||||
or
|
||||
operand instanceof PhiInputOperand and
|
||||
resultEscapesNonReturn(operand.getUse())
|
||||
or
|
||||
operandEscapesDomain(operand)
|
||||
}
|
||||
|
||||
@@ -237,6 +236,9 @@ 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) {
|
||||
@@ -338,56 +340,6 @@ 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
|
||||
@@ -396,14 +348,12 @@ private predicate operandConsumesEscaped(Configuration::Allocation allocation) {
|
||||
predicate allocationEscapes(Configuration::Allocation allocation) {
|
||||
allocation.alwaysEscapes()
|
||||
or
|
||||
exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() |
|
||||
resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
or
|
||||
operandConsumesEscaped(allocation)
|
||||
exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
)
|
||||
or
|
||||
Configuration::phaseNeedsSoundEscapeAnalysis() and
|
||||
(resultEscapesNonReturn(allocation.getABaseInstruction()) or operandConsumesEscaped(allocation))
|
||||
resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
private import AliasConfigurationImports
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* A memory allocation that can be tracked by the SimpleSSA alias analysis.
|
||||
@@ -17,5 +16,3 @@ class Allocation extends IRAutomaticVariable {
|
||||
}
|
||||
|
||||
predicate phaseNeedsSoundEscapeAnalysis() { any() }
|
||||
|
||||
Unit getOldAllocation(Allocation allocation) { any() }
|
||||
|
||||
@@ -15,51 +15,6 @@ 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
|
||||
@@ -77,11 +32,6 @@ private module Cached {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiNodeAfterUninitializedGroup(UninitializedGroupInstruction initGroup) {
|
||||
hasChiNodeAfterUninitializedGroup(_, initGroup)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
@@ -95,8 +45,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction or
|
||||
TUninitializedGroupInstruction;
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
/**
|
||||
* If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block,
|
||||
@@ -129,8 +78,6 @@ private module Cached {
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUninitializedGroupInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
@@ -176,8 +123,7 @@ private module Cached {
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
canModelResultForOldInstruction(getOldInstruction(instruction)) or
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction or // Chis always have modeled results
|
||||
instruction instanceof UninitializedGroupInstruction // Group initializers always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -188,23 +134,16 @@ 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(Instruction input | instruction = getChi(input) |
|
||||
Alias::getResultMemoryLocation(input).getVirtualVariable() instanceof
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).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.getAnAllocation())
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -266,11 +205,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
(
|
||||
instruction = getChi(getOldInstruction(result))
|
||||
or
|
||||
instruction = getChi(result.(UninitializedGroupInstruction))
|
||||
) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -328,14 +263,6 @@ 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.
|
||||
@@ -389,19 +316,6 @@ 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(
|
||||
@@ -415,19 +329,6 @@ 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
|
||||
@@ -443,40 +344,14 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private UninitializedGroupInstruction firstInstructionToUninitializedGroup(
|
||||
Instruction instruction, EdgeKind kind
|
||||
) {
|
||||
exists(IRFunction func |
|
||||
isFirstInstructionBeforeUninitializedGroup(instruction, func) and
|
||||
result = getInitGroupInstruction(0, func) and
|
||||
kind instanceof GotoEdge
|
||||
)
|
||||
}
|
||||
/*
|
||||
* 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 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
|
||||
) {
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
@@ -496,107 +371,6 @@ 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 |
|
||||
@@ -632,16 +406,6 @@ 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
|
||||
@@ -654,16 +418,9 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
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()
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
instr = reusedPhiInstruction(_) and
|
||||
@@ -691,8 +448,6 @@ 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
|
||||
}
|
||||
|
||||
@@ -705,15 +460,10 @@ private module Cached {
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
exists(Instruction primaryInstr |
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
exists(Alias::VariableGroup vg |
|
||||
instr = uninitializedGroup(vg) and
|
||||
result = vg.getIRFunction()
|
||||
)
|
||||
or
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
@@ -728,8 +478,6 @@ private module Cached {
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
or
|
||||
instruction = getChi(result.(UninitializedGroupInstruction))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,7 +485,7 @@ private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(Instruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
@@ -758,16 +506,6 @@ 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
|
||||
|
||||
/**
|
||||
@@ -930,37 +668,19 @@ 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`.
|
||||
*/
|
||||
private Instruction getDefinitionOrChiInstruction0(
|
||||
Instruction getDefinitionOrChiInstruction(
|
||||
OldBlock defBlock, int defOffset, Alias::MemoryLocation defLocation,
|
||||
Alias::MemoryLocation actualDefLocation
|
||||
) {
|
||||
exists(OldInstruction oldInstr, int oldOffset | oldInstr = defBlock.getInstruction(oldOffset) |
|
||||
exists(OldInstruction oldInstr, int oldOffset |
|
||||
oldInstr = defBlock.getInstruction(oldOffset) and
|
||||
oldOffset >= 0
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = getChiOffset(oldOffset, defBlock) and
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
@@ -969,7 +689,7 @@ module DefUse {
|
||||
actualDefLocation = defLocation.getVirtualVariable()
|
||||
or
|
||||
// An even offset corresponds to the original instruction.
|
||||
defOffset = getNonChiOffset(oldOffset, defBlock) and
|
||||
defOffset = oldOffset * 2 and
|
||||
result = getNewInstruction(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
@@ -982,54 +702,6 @@ 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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1170,20 +842,8 @@ module DefUse {
|
||||
block.getInstruction(index) = def and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation) and
|
||||
if overlap instanceof MayPartiallyOverlap
|
||||
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.
|
||||
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.
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1244,11 +904,10 @@ module DefUse {
|
||||
block.getInstruction(index) = use and
|
||||
(
|
||||
// A direct use of the location.
|
||||
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and
|
||||
offset = getNonChiOffset(index, block)
|
||||
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and offset = index * 2
|
||||
or
|
||||
// A `Chi` instruction will include a use of the virtual variable.
|
||||
hasChiNode(useLocation, use) and offset = getChiOffset(index, block)
|
||||
hasChiNode(useLocation, use) and offset = (index * 2) + 1
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1398,9 +1057,5 @@ module Ssa {
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasChiNodeAfterUninitializedGroup = Cached::hasChiNodeAfterUninitializedGroup/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
|
||||
class VariableGroup = Alias::VariableGroup;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import AliasAnalysis
|
||||
private import SimpleSSAImports
|
||||
import SimpleSSAPublicImports
|
||||
import AliasConfiguration
|
||||
private import codeql.util.Unit
|
||||
private import AliasConfiguration
|
||||
|
||||
private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRType type) {
|
||||
exists(Instruction constantBase, int bitOffset |
|
||||
@@ -49,7 +48,7 @@ predicate canReuseSsaForVariable(IRAutomaticVariable var) {
|
||||
|
||||
private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) }
|
||||
|
||||
private MemoryLocation getMemoryLocation(Allocation var) { result.getAnAllocation() = var }
|
||||
private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var }
|
||||
|
||||
class MemoryLocation extends TMemoryLocation {
|
||||
Allocation var;
|
||||
@@ -58,7 +57,7 @@ class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
final string toString() { result = var.getAllocationString() }
|
||||
|
||||
final Allocation getAnAllocation() { result = var }
|
||||
final Allocation getAllocation() { result = var }
|
||||
|
||||
final Language::Location getLocation() { result = var.getLocation() }
|
||||
|
||||
@@ -78,40 +77,6 @@ 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,11 +27,6 @@ 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
|
||||
|
||||
@@ -86,41 +86,6 @@ 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`.
|
||||
@@ -614,43 +579,23 @@ 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 Function {
|
||||
class BeginFunction extends MemberFunction {
|
||||
BeginFunction() {
|
||||
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
|
||||
)
|
||||
this.hasName(["begin", "cbegin", "rbegin", "crbegin", "before_begin", "cbefore_begin"]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private string endName() { result = ["end", "cend", "rend", "crend"] }
|
||||
|
||||
/**
|
||||
* An `end` member function, or a related function, that returns an iterator.
|
||||
*/
|
||||
class EndFunction extends Function {
|
||||
class EndFunction extends MemberFunction {
|
||||
EndFunction() {
|
||||
this.getUnspecifiedType() instanceof Iterator and
|
||||
(
|
||||
this.hasName(endName()) and
|
||||
this instanceof MemberFunction
|
||||
or
|
||||
this.hasGlobalOrStdOrBslName(endName()) and
|
||||
this instanceof MemberFunction and
|
||||
this.getNumberOfParameters() = 1
|
||||
)
|
||||
this.hasName(["end", "cend", "rend", "crend"]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,7 +603,7 @@ class EndFunction extends Function {
|
||||
* A `begin` or `end` member function, or a related member function, that
|
||||
* returns an iterator.
|
||||
*/
|
||||
class BeginOrEndFunction extends Function {
|
||||
class BeginOrEndFunction extends MemberFunction {
|
||||
BeginOrEndFunction() {
|
||||
this instanceof BeginFunction or
|
||||
this instanceof EndFunction
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
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.
|
||||
@@ -97,7 +96,7 @@ private class Sprintf extends FormattingFunction {
|
||||
/**
|
||||
* Implements `Snprintf`.
|
||||
*/
|
||||
private class SnprintfImpl extends Snprintf, AliasFunction, SideEffectFunction {
|
||||
private class SnprintfImpl extends Snprintf {
|
||||
SnprintfImpl() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
@@ -144,26 +143,6 @@ private class SnprintfImpl extends Snprintf, AliasFunction, SideEffectFunction {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
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
|
||||
@@ -60,7 +58,7 @@ private class Vector extends StdSequenceContainer {
|
||||
/**
|
||||
* The standard container functions `push_back` and `push_front`.
|
||||
*/
|
||||
class StdSequenceContainerPush extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
class StdSequenceContainerPush extends MemberFunction {
|
||||
StdSequenceContainerPush() {
|
||||
this.getClassAndName("push_back") instanceof Vector or
|
||||
this.getClassAndName(["push_back", "push_front"]) instanceof Deque or
|
||||
@@ -76,115 +74,12 @@ class StdSequenceContainerPush extends MemberFunction, SideEffectFunction, Alias
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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 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, SideEffectFunction, AliasFunction {
|
||||
class StdSequenceContainerInsert extends MemberFunction {
|
||||
StdSequenceContainerInsert() {
|
||||
this.getClassAndName("insert") instanceof Deque or
|
||||
this.getClassAndName("insert") instanceof List or
|
||||
@@ -205,138 +100,17 @@ class StdSequenceContainerInsert extends MemberFunction, SideEffectFunction, Ali
|
||||
* 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 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 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `at` and `operator[]`.
|
||||
*/
|
||||
class StdSequenceContainerAt extends MemberFunction, SideEffectFunction, AliasFunction {
|
||||
class StdSequenceContainerAt extends MemberFunction {
|
||||
StdSequenceContainerAt() {
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Array or
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
|
||||
this.getClassAndName(["at", "operator[]"]) 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) {
|
||||
none()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,115 +142,6 @@ 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.
|
||||
*/
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* 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,8 +5,6 @@
|
||||
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.
|
||||
@@ -18,9 +16,7 @@ private class MapOrUnorderedMap extends Class {
|
||||
/**
|
||||
* Additional model for map constructors using iterator inputs.
|
||||
*/
|
||||
private class StdMapConstructor extends Constructor, TaintFunction, AliasFunction,
|
||||
SideEffectFunction
|
||||
{
|
||||
private class StdMapConstructor extends Constructor, TaintFunction {
|
||||
StdMapConstructor() { this.getDeclaringType() instanceof MapOrUnorderedMap }
|
||||
|
||||
/**
|
||||
@@ -39,23 +35,6 @@ private class StdMapConstructor extends Constructor, TaintFunction, AliasFunctio
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +133,7 @@ class StdMapAt extends MemberFunction {
|
||||
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
|
||||
}
|
||||
|
||||
private class StdMapAtModels extends StdMapAt, TaintFunction, AliasFunction, SideEffectFunction {
|
||||
private class StdMapAtModels extends StdMapAt, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
input.isQualifierObject() and
|
||||
@@ -165,18 +144,6 @@ private class StdMapAtModels extends StdMapAt, TaintFunction, AliasFunction, Sid
|
||||
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() }
|
||||
}
|
||||
|
||||
@@ -220,63 +187,3 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* 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,8 +3,6 @@
|
||||
*/
|
||||
|
||||
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>`.
|
||||
@@ -39,9 +37,7 @@ class StdPairCopyishConstructor extends Constructor, TaintFunction {
|
||||
/**
|
||||
* Additional model for `std::pair` constructors.
|
||||
*/
|
||||
private class StdPairConstructor extends Constructor, TaintFunction, AliasFunction,
|
||||
SideEffectFunction
|
||||
{
|
||||
private class StdPairConstructor extends Constructor, TaintFunction {
|
||||
StdPairConstructor() { this.getDeclaringType() instanceof StdPair }
|
||||
|
||||
/**
|
||||
@@ -63,77 +59,4 @@ private class StdPairConstructor extends Constructor, TaintFunction, AliasFuncti
|
||||
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,8 +7,6 @@
|
||||
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.
|
||||
@@ -68,7 +66,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).getUnspecifiedType() instanceof Iterator
|
||||
this.getParameter(result).getType() instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +78,7 @@ abstract private class StdStringTaintFunction extends TaintFunction {
|
||||
* std::string b(a.begin(), a.end());
|
||||
* ```
|
||||
*/
|
||||
private class StdStringConstructor extends Constructor, StdStringTaintFunction, SideEffectFunction,
|
||||
AliasFunction
|
||||
{
|
||||
private class StdStringConstructor extends Constructor, StdStringTaintFunction {
|
||||
StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -98,42 +94,6 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +164,7 @@ private class StdStringFrontBack extends StdStringTaintFunction {
|
||||
/**
|
||||
* The (non-member) `std::string` function `operator+`.
|
||||
*/
|
||||
private class StdStringPlus extends StdStringTaintFunction, SideEffectFunction, AliasFunction {
|
||||
private class StdStringPlus extends StdStringTaintFunction {
|
||||
StdStringPlus() {
|
||||
this.hasQualifiedName(["std", "bsl"], "operator+") and
|
||||
this.getUnspecifiedType() instanceof StdBasicString
|
||||
@@ -218,22 +178,6 @@ private class StdStringPlus extends StdStringTaintFunction, SideEffectFunction,
|
||||
) 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() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,7 +185,7 @@ private class StdStringPlus extends StdStringTaintFunction, SideEffectFunction,
|
||||
* All of these functions combine the existing string with a new
|
||||
* string (or character) from one of the arguments.
|
||||
*/
|
||||
private class StdStringAppend extends StdStringTaintFunction, SideEffectFunction, AliasFunction {
|
||||
private class StdStringAppend extends StdStringTaintFunction {
|
||||
StdStringAppend() {
|
||||
this.getClassAndName(["operator+=", "append", "replace"]) instanceof StdBasicString
|
||||
}
|
||||
@@ -266,22 +210,6 @@ private class StdStringAppend extends StdStringTaintFunction, SideEffectFunction
|
||||
}
|
||||
|
||||
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() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,7 +301,7 @@ private class StdStringSubstr extends StdStringTaintFunction {
|
||||
/**
|
||||
* The `std::string` functions `at` and `operator[]`.
|
||||
*/
|
||||
private class StdStringAt extends StdStringTaintFunction, SideEffectFunction, AliasFunction {
|
||||
private class StdStringAt extends StdStringTaintFunction {
|
||||
StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -387,22 +315,6 @@ private class StdStringAt extends StdStringTaintFunction, SideEffectFunction, Al
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,54 +324,6 @@ 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).
|
||||
*/
|
||||
@@ -678,33 +542,6 @@ 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`.
|
||||
|
||||
@@ -409,11 +409,6 @@ function_defaulted(unique int id: @function ref);
|
||||
|
||||
function_prototyped(unique int id: @function ref)
|
||||
|
||||
deduction_guide_for_class(
|
||||
int id: @function ref,
|
||||
int class_template: @usertype ref
|
||||
)
|
||||
|
||||
member_function_this_type(
|
||||
unique int id: @function ref,
|
||||
int this_type: @type ref
|
||||
@@ -490,17 +485,10 @@ namespace_decls(
|
||||
int bodylocation: @location_default ref
|
||||
);
|
||||
|
||||
case @using.kind of
|
||||
1 = @using_declaration
|
||||
| 2 = @using_directive
|
||||
| 3 = @using_enum_declaration
|
||||
;
|
||||
|
||||
usings(
|
||||
unique int id: @using,
|
||||
int element_id: @element ref,
|
||||
int location: @location_default ref,
|
||||
int kind: int ref
|
||||
int location: @location_default ref
|
||||
);
|
||||
|
||||
/** The element which contains the `using` declaration. */
|
||||
@@ -921,11 +909,6 @@ varspecifiers(
|
||||
int spec_id: @specifier ref
|
||||
);
|
||||
|
||||
explicit_specifier_exprs(
|
||||
unique int func_id: @function ref,
|
||||
int constant: @expr ref
|
||||
)
|
||||
|
||||
attributes(
|
||||
unique int id: @attribute,
|
||||
int kind: int ref,
|
||||
@@ -1375,8 +1358,6 @@ funbind(
|
||||
@assign_expr = @assignexpr | @assign_op_expr | @blockassignexpr
|
||||
|
||||
/*
|
||||
Binary encoding of the allocator form.
|
||||
|
||||
case @allocator.form of
|
||||
0 = plain
|
||||
| 1 = alignment
|
||||
@@ -1395,13 +1376,11 @@ expr_allocator(
|
||||
);
|
||||
|
||||
/*
|
||||
Binary encoding of the deallocator form.
|
||||
|
||||
case @deallocator.form of
|
||||
0 = plain
|
||||
| 1 = size
|
||||
| 2 = alignment
|
||||
| 4 = destroying_delete
|
||||
| 3 = size_and_alignment
|
||||
;
|
||||
*/
|
||||
|
||||
@@ -1769,25 +1748,6 @@ case @expr.kind of
|
||||
| 361 = @isvoid
|
||||
| 362 = @isvolatile
|
||||
| 363 = @reuseexpr
|
||||
| 364 = @istriviallycopyassignable
|
||||
| 365 = @isassignablenopreconditioncheck
|
||||
| 366 = @referencebindstotemporary
|
||||
| 367 = @issameas
|
||||
| 368 = @builtinhasattribute
|
||||
| 369 = @ispointerinterconvertiblewithclass
|
||||
| 370 = @builtinispointerinterconvertiblewithclass
|
||||
| 371 = @iscorrespondingmember
|
||||
| 372 = @builtiniscorrespondingmember
|
||||
| 373 = @isboundedarray
|
||||
| 374 = @isunboundedarray
|
||||
| 375 = @isreferenceable
|
||||
| 378 = @isnothrowconvertible
|
||||
| 379 = @referenceconstructsfromtemporary
|
||||
| 380 = @referenceconvertsfromtemporary
|
||||
| 381 = @isconvertible
|
||||
| 382 = @isvalidwinrttype
|
||||
| 383 = @iswinclass
|
||||
| 384 = @iswininterface
|
||||
;
|
||||
|
||||
@var_args_expr = @vastartexpr
|
||||
@@ -1882,25 +1842,6 @@ case @expr.kind of
|
||||
| @isunsigned
|
||||
| @isvoid
|
||||
| @isvolatile
|
||||
| @istriviallycopyassignable
|
||||
| @isassignablenopreconditioncheck
|
||||
| @referencebindstotemporary
|
||||
| @issameas
|
||||
| @builtinhasattribute
|
||||
| @ispointerinterconvertiblewithclass
|
||||
| @builtinispointerinterconvertiblewithclass
|
||||
| @iscorrespondingmember
|
||||
| @builtiniscorrespondingmember
|
||||
| @isboundedarray
|
||||
| @isunboundedarray
|
||||
| @isreferenceable
|
||||
| @isnothrowconvertible
|
||||
| @referenceconstructsfromtemporary
|
||||
| @referenceconvertsfromtemporary
|
||||
| @isconvertible
|
||||
| @isvalidwinrttype
|
||||
| @iswinclass
|
||||
| @iswininterface
|
||||
;
|
||||
|
||||
new_allocated_type(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Support destroying deletes
|
||||
compatibility: partial
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
description: Support using-enum declarations.
|
||||
compatibility: partial
|
||||
usings.rel: run usings.qlo
|
||||
@@ -1,17 +0,0 @@
|
||||
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) and
|
||||
if target instanceof @namespace then kind = 2 else kind = 1
|
||||
select u, target, loc, kind
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Add relation between deduction guides and class templates
|
||||
compatibility: partial
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Add new builtin operations
|
||||
compatibility: backwards
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Support explicit(bool) specifiers
|
||||
compatibility: partial
|
||||
@@ -1,27 +1,3 @@
|
||||
## 1.2.0
|
||||
|
||||
### Query Metadata Changes
|
||||
|
||||
* The precision of `cpp/unsigned-difference-expression-compared-zero` ("Unsigned difference expression compared to zero") has been increased to `high`. As a result, it will be run by default as part of the Code Scanning suite.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed false positives in the `cpp/memory-may-not-be-freed` ("Memory may not be freed") query involving class methods that returned an allocated field of that class being misidentified as allocators.
|
||||
* The `cpp/incorrectly-checked-scanf` ("Incorrect return-value check for a 'scanf'-like function") query now produces fewer false positive results.
|
||||
* The `cpp/incorrect-allocation-error-handling` ("Incorrect allocation-error handling") query no longer produces occasional false positive results inside template instantiations.
|
||||
* The `cpp/suspicious-allocation-size` ("Not enough memory allocated for array of pointer type") query no longer produces false positives on "variable size" `struct`s.
|
||||
|
||||
## 1.1.0
|
||||
|
||||
### Query Metadata Changes
|
||||
|
||||
* The precision of `cpp/iterator-to-expired-container` ("Iterator to expired container") has been increased to `high`. As a result, it will be run by default as part of the Code Scanning suite.
|
||||
* The precision of `cpp/unsafe-strncat` ("Potentially unsafe call to strncat") has been increased to `high`. As a result, it will be run by default as part of the Code Scanning suite.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `cpp/unsigned-difference-expression-compared-zero` ("Unsigned difference expression compared to zero") query now produces fewer false positives.
|
||||
|
||||
## 1.0.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -39,7 +39,7 @@ predicate allocCallOrIndirect(Expr e) {
|
||||
allocCallOrIndirect(rtn.getExpr())
|
||||
or
|
||||
// return variable assigned with alloc
|
||||
exists(StackVariable v |
|
||||
exists(Variable v |
|
||||
v = rtn.getExpr().(VariableAccess).getTarget() and
|
||||
allocCallOrIndirect(v.getAnAssignedValue()) and
|
||||
not assignedToFieldOrGlobal(v, _)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user