mirror of
https://github.com/github/codeql.git
synced 2026-05-20 22:27:18 +02:00
Compare commits
1 Commits
codeql-cli
...
max-schaef
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d62d8ffed |
3
.bazelrc
3
.bazelrc
@@ -14,7 +14,4 @@ build:linux --cxxopt=-std=c++20
|
||||
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
|
||||
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
|
||||
|
||||
common --registry=file:///%workspace%/misc/bazel/registry
|
||||
common --registry=https://bcr.bazel.build
|
||||
|
||||
try-import %workspace%/local.bazelrc
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# this file should contain bazel settings required to build things from `semmle-code`
|
||||
|
||||
common --registry=file:///%workspace%/ql/misc/bazel/registry
|
||||
common --registry=https://bcr.bazel.build
|
||||
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -67,6 +67,11 @@ go/extractor/opencsv/CSVReader.java -text
|
||||
# for those testing dbscheme files.
|
||||
*/ql/lib/upgrades/initial/*.dbscheme -text
|
||||
|
||||
# Generated test files - these are synced from the standard JavaScript libraries using
|
||||
# `javascript/ql/experimental/adaptivethreatmodeling/test/update_endpoint_test_files.py`.
|
||||
javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/**/*.js linguist-generated=true -merge
|
||||
javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/**/*.ts linguist-generated=true -merge
|
||||
|
||||
# Auto-generated modeling for Python
|
||||
python/ql/lib/semmle/python/frameworks/data/internal/subclass-capture/*.yml linguist-generated=true
|
||||
|
||||
|
||||
5
.github/labeler.yml
vendored
5
.github/labeler.yml
vendored
@@ -15,7 +15,7 @@ Java:
|
||||
- change-notes/**/*java.*
|
||||
|
||||
JS:
|
||||
- any: [ 'javascript/**/*' ]
|
||||
- any: [ 'javascript/**/*', '!javascript/ql/experimental/adaptivethreatmodeling/**/*' ]
|
||||
- change-notes/**/*javascript*
|
||||
|
||||
Kotlin:
|
||||
@@ -46,3 +46,6 @@ documentation:
|
||||
# Since these are all shared files that need to be synced, just pick _one_ copy of each.
|
||||
"DataFlow Library":
|
||||
- "shared/dataflow/**/*"
|
||||
|
||||
"ATM":
|
||||
- javascript/ql/experimental/adaptivethreatmodeling/**/*
|
||||
|
||||
28
.github/workflows/buildifier.yml
vendored
28
.github/workflows/buildifier.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: Check bazel formatting
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.bazel"
|
||||
- "**.bzl"
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Check bazel formatting
|
||||
uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507
|
||||
with:
|
||||
extra_args: >
|
||||
buildifier --all-files 2>&1 ||
|
||||
(
|
||||
echo -e "In order to format all bazel files, please run:\n bazel run //:buildifier"; exit 1
|
||||
)
|
||||
3
.github/workflows/swift.yml
vendored
3
.github/workflows/swift.yml
vendored
@@ -6,7 +6,6 @@ on:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "misc/codegen/**"
|
||||
- "shared/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift.yml
|
||||
- .github/actions/**
|
||||
@@ -23,12 +22,10 @@ on:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "misc/codegen/**"
|
||||
- "shared/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
- .pre-commit-config.yaml
|
||||
- "!**/*.md"
|
||||
- "!**/*.qhelp"
|
||||
branches:
|
||||
|
||||
@@ -20,15 +20,13 @@ repos:
|
||||
- id: autopep8
|
||||
files: ^misc/codegen/.*\.py
|
||||
|
||||
- repo: local
|
||||
- repo: https://github.com/warchant/pre-commit-buildifier
|
||||
rev: 0.0.2
|
||||
hooks:
|
||||
- id: buildifier
|
||||
name: Format bazel files
|
||||
files: \.(bazel|bzl)
|
||||
language: system
|
||||
entry: bazel run //:buildifier
|
||||
pass_filenames: false
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: codeql-format
|
||||
name: Fix QL file formatting
|
||||
files: \.qll?$
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
load("@buildifier_prebuilt//:rules.bzl", "buildifier")
|
||||
|
||||
buildifier(
|
||||
name = "buildifier",
|
||||
exclude_patterns = [
|
||||
"./.git/*",
|
||||
],
|
||||
lint_mode = "fix",
|
||||
)
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
/java/ql/test-kotlin1/ @github/codeql-kotlin
|
||||
/java/ql/test-kotlin2/ @github/codeql-kotlin
|
||||
|
||||
# ML-powered queries
|
||||
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
|
||||
|
||||
# CodeQL tools and associated docs
|
||||
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
|
||||
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
@@ -34,7 +37,9 @@ MODULE.bazel @github/codeql-ci-reviewers
|
||||
|
||||
# Workflows
|
||||
/.github/workflows/ @github/codeql-ci-reviewers
|
||||
/.github/workflows/atm-* @github/codeql-ml-powered-queries-reviewers
|
||||
/.github/workflows/go-* @github/codeql-go
|
||||
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers
|
||||
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
|
||||
/.github/workflows/ruby-* @github/codeql-ruby
|
||||
/.github/workflows/swift.yml @github/codeql-swift
|
||||
|
||||
@@ -14,7 +14,7 @@ local_path_override(
|
||||
# see https://registry.bazel.build/ for a list of available packages
|
||||
|
||||
bazel_dep(name = "platforms", version = "0.0.8")
|
||||
bazel_dep(name = "rules_pkg", version = "0.10.1")
|
||||
bazel_dep(name = "rules_pkg", version = "0.9.1")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.0.3")
|
||||
bazel_dep(name = "rules_python", version = "0.31.0")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.5.0")
|
||||
@@ -22,8 +22,6 @@ 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 = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
|
||||
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
|
||||
pip.parse(
|
||||
hub_name = "codegen_deps",
|
||||
@@ -33,8 +31,6 @@ pip.parse(
|
||||
use_repo(pip, "codegen_deps")
|
||||
|
||||
swift_deps = use_extension("//swift/third_party:load.bzl", "swift_deps")
|
||||
|
||||
# following list can be kept in sync with `bazel mod tidy`
|
||||
use_repo(
|
||||
swift_deps,
|
||||
"binlog",
|
||||
|
||||
@@ -11,6 +11,16 @@ provide:
|
||||
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
|
||||
- "go/ql/config/legacy-support/qlpack.yml"
|
||||
- "go/build/codeql-extractor-go/codeql-extractor.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml"
|
||||
# This pack is explicitly excluded from the workspace since most users
|
||||
# will want to use a version of this pack from the package cache. Internal
|
||||
# users can uncomment the following line and place a custom ML model
|
||||
# in the corresponding pack to test a custom ML model within their local
|
||||
# checkout.
|
||||
# - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/lib/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/src/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/test/qlpack.yml"
|
||||
|
||||
@@ -251,6 +251,12 @@
|
||||
"cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/SafeExternalAPIFunction.qll"
|
||||
],
|
||||
"XML": [
|
||||
"cpp/ql/lib/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/XML.qll",
|
||||
"java/ql/lib/semmle/code/xml/XML.qll",
|
||||
"python/ql/lib/semmle/python/xml/XML.qll"
|
||||
],
|
||||
"DuplicationProblems.inc.qhelp": [
|
||||
"cpp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp",
|
||||
"javascript/ql/src/Metrics/DuplicationProblems.inc.qhelp",
|
||||
@@ -362,7 +368,7 @@
|
||||
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
|
||||
],
|
||||
"Python model summaries test extension": [
|
||||
"python/ql/test/library-tests/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||
"python/ql/test/library-tests/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup")
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_filegroup")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -203,8 +203,6 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
public IList<DiagnosticMessage> Diagnostics { get; } = new List<DiagnosticMessage>();
|
||||
|
||||
public void AddEntry(DiagnosticMessage message) => this.Diagnostics.Add(message);
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -252,7 +250,12 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
EndCallbackIn.Add(s);
|
||||
}
|
||||
|
||||
CppAutobuilder CreateAutoBuilder(bool isWindows, string? dotnetVersion = null, string cwd = @"C:\Project")
|
||||
CppAutobuilder CreateAutoBuilder(bool isWindows,
|
||||
string? buildless = null, string? solution = null, string? buildCommand = null, string? ignoreErrors = null,
|
||||
string? msBuildArguments = null, string? msBuildPlatform = null, string? msBuildConfiguration = null, string? msBuildTarget = null,
|
||||
string? dotnetArguments = null, string? dotnetVersion = null, string? vsToolsVersion = null,
|
||||
string? nugetRestore = null, string? allSolutions = null,
|
||||
string cwd = @"C:\Project")
|
||||
{
|
||||
string codeqlUpperLanguage = Language.Cpp.UpperCaseName;
|
||||
Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false";
|
||||
@@ -262,7 +265,22 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_DIAGNOSTIC_DIR"] = "";
|
||||
Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java";
|
||||
Actions.GetEnvironmentVariable["CODEQL_PLATFORM"] = "win64";
|
||||
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_OPTION_DOTNET_VERSION"] = dotnetVersion;
|
||||
Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa";
|
||||
Actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java";
|
||||
Actions.GetEnvironmentVariable["SEMMLE_PLATFORM_TOOLS"] = @"C:\odasa\tools";
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_ARGUMENTS"] = msBuildArguments;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_PLATFORM"] = msBuildPlatform;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_CONFIGURATION"] = msBuildConfiguration;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_TARGET"] = msBuildTarget;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_ARGUMENTS"] = dotnetArguments;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_VERSION"] = dotnetVersion;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_BUILD_COMMAND"] = buildCommand;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_SOLUTION"] = solution;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_IGNORE_ERRORS"] = ignoreErrors;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_BUILDLESS"] = buildless;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_ALL_SOLUTIONS"] = allSolutions;
|
||||
Actions.GetEnvironmentVariable["LGTM_INDEX_NUGET_RESTORE"] = nugetRestore;
|
||||
Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = isWindows ? @"C:\Program Files (x86)" : null;
|
||||
Actions.GetCurrentDirectory = cwd;
|
||||
Actions.IsWindows = isWindows;
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace Semmle.Autobuild.Cpp
|
||||
|
||||
public override BuildScript GetBuildScript()
|
||||
{
|
||||
if (Options.BuildCommand != null)
|
||||
return new BuildCommandRule((_, f) => f(null)).Analyse(this, false);
|
||||
|
||||
return
|
||||
// First try MSBuild
|
||||
new MsBuildRule().Analyse(this, true) |
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Semmle.Autobuild.Cpp
|
||||
try
|
||||
{
|
||||
Console.WriteLine("CodeQL C++ autobuilder");
|
||||
using var builder = new CppAutobuilder(actions, options);
|
||||
var builder = new CppAutobuilder(actions, options);
|
||||
return builder.AttemptBuild();
|
||||
}
|
||||
catch (InvalidEnvironmentException ex)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix")
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
|
||||
|
||||
pkg_files(
|
||||
name = "downgrades",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_files")
|
||||
|
||||
package(default_visibility = ["//cpp:__pkg__"])
|
||||
|
||||
|
||||
@@ -1,24 +1,3 @@
|
||||
## 0.13.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Deleted the deprecated `GlobalValueNumberingImpl.qll` implementation.
|
||||
|
||||
### New Features
|
||||
|
||||
* Models-as-Data support has been added for C/C++. This feature allows flow sources, sinks and summaries to be expressed in compact strings as an alternative to modelling each source / sink / summary with explicit QL. See `dataflow/ExternalFlow.qll` for documentation and specification of the model format, and `models/implementations/ZMQ.qll` for a simple example of models. Importing models from `.yml` is not yet supported.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Source models have been added for the standard library function `getc` (and variations).
|
||||
* Source, sink and flow models for the ZeroMQ (ZMQ) networking library have been added.
|
||||
* Parameters of functions without definitions now have `ParameterNode`s.
|
||||
* The alias analysis used internally by various libraries has been improved to answer alias questions more conservatively. As a result, some queries may report fewer false positives.
|
||||
|
||||
## 0.12.11
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.12.10
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.12.11
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,16 +0,0 @@
|
||||
## 0.13.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Deleted the deprecated `GlobalValueNumberingImpl.qll` implementation.
|
||||
|
||||
### New Features
|
||||
|
||||
* Models-as-Data support has been added for C/C++. This feature allows flow sources, sinks and summaries to be expressed in compact strings as an alternative to modelling each source / sink / summary with explicit QL. See `dataflow/ExternalFlow.qll` for documentation and specification of the model format, and `models/implementations/ZMQ.qll` for a simple example of models. Importing models from `.yml` is not yet supported.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Source models have been added for the standard library function `getc` (and variations).
|
||||
* Source, sink and flow models for the ZeroMQ (ZMQ) networking library have been added.
|
||||
* Parameters of functions without definitions now have `ParameterNode`s.
|
||||
* The alias analysis used internally by various libraries has been improved to answer alias questions more conservatively. As a result, some queries may report fewer false positives.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.13.0
|
||||
lastReleaseVersion: 0.12.10
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.13.0
|
||||
version: 0.12.10
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
@@ -7,11 +7,8 @@ library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/rangeanalysis: ${workspace}
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/typeflow: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
codeql/xml: ${workspace}
|
||||
warnOnImplicitThis: true
|
||||
|
||||
@@ -364,8 +364,6 @@ class ConversionNode extends ExprNode {
|
||||
childIndex = 0 and
|
||||
result.getAst() = conv.getExpr() and
|
||||
conv.getExpr() instanceof Conversion
|
||||
or
|
||||
result.getAst() = expr.getImplicitDestructorCall(childIndex - 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,25 +461,6 @@ class StmtNode extends AstNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a child of a `Stmt` that is itself a `Stmt`.
|
||||
*/
|
||||
class ChildStmtNode extends StmtNode {
|
||||
Stmt childStmt;
|
||||
|
||||
ChildStmtNode() { exists(Stmt parent | parent.getAChild() = childStmt and childStmt = ast) }
|
||||
|
||||
override BaseAstNode getChildInternal(int childIndex) {
|
||||
result = super.getChildInternal(childIndex)
|
||||
or
|
||||
exists(int destructorIndex |
|
||||
result.getAst() = childStmt.getImplicitDestructorCall(destructorIndex) and
|
||||
childIndex =
|
||||
destructorIndex + max(int index | exists(childStmt.getChild(index)) or index = 0) + 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `DeclStmt`.
|
||||
*/
|
||||
@@ -693,13 +672,6 @@ class FunctionNode extends FunctionOrGlobalOrNamespaceVariableNode {
|
||||
private string getChildAccessorWithoutConversions(Locatable parent, Element child) {
|
||||
shouldPrintDeclaration(getAnEnclosingDeclaration(parent)) and
|
||||
(
|
||||
exists(Stmt s, int i | s.getChild(i) = parent |
|
||||
exists(int n |
|
||||
s.getChild(i).(Stmt).getImplicitDestructorCall(n) = child and
|
||||
result = "getImplicitDestructorCall(" + n + ")"
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Stmt s | s = parent |
|
||||
namedStmtChildPredicates(s, child, result)
|
||||
or
|
||||
@@ -890,7 +862,7 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
|
||||
or
|
||||
expr.(DeleteOrDeleteArrayExpr).getDestructorCall() = ele and pred = "getDestructorCall()"
|
||||
or
|
||||
expr.(DeleteOrDeleteArrayExpr).getExprWithReuse() = ele and pred = "getExprWithReuse()"
|
||||
expr.(DeleteOrDeleteArrayExpr).getExpr() = ele and pred = "getExpr()"
|
||||
or
|
||||
expr.(DestructorFieldDestruction).getExpr() = ele and pred = "getExpr()"
|
||||
or
|
||||
|
||||
@@ -3,67 +3,305 @@
|
||||
*/
|
||||
|
||||
import semmle.files.FileSystem
|
||||
private import codeql.xml.Xml
|
||||
|
||||
private module Input implements InputSig<File, Location> {
|
||||
class XmlLocatableBase = @xmllocatable or @xmlnamespaceable;
|
||||
private class TXmlLocatable =
|
||||
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
|
||||
|
||||
predicate xmllocations_(XmlLocatableBase e, Location loc) { xmllocations(e, loc) }
|
||||
/** An XML element that has a location. */
|
||||
class XmlLocatable extends @xmllocatable, TXmlLocatable {
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { xmllocations(this, result) }
|
||||
|
||||
class XmlParentBase = @xmlparent;
|
||||
|
||||
class XmlNamespaceableBase = @xmlnamespaceable;
|
||||
|
||||
class XmlElementBase = @xmlelement;
|
||||
|
||||
class XmlFileBase = File;
|
||||
|
||||
predicate xmlEncoding_(XmlFileBase f, string enc) { xmlEncoding(f, enc) }
|
||||
|
||||
class XmlDtdBase = @xmldtd;
|
||||
|
||||
predicate xmlDTDs_(XmlDtdBase e, string root, string publicId, string systemId, XmlFileBase file) {
|
||||
xmlDTDs(e, root, publicId, systemId, file)
|
||||
}
|
||||
|
||||
predicate xmlElements_(
|
||||
XmlElementBase e, string name, XmlParentBase parent, int idx, XmlFileBase file
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
xmlElements(e, name, parent, idx, file)
|
||||
exists(File f, Location l | l = this.getLocation() |
|
||||
locations_default(l, f, startline, startcolumn, endline, endcolumn) and
|
||||
filepath = f.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
||||
class XmlAttributeBase = @xmlattribute;
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
predicate xmlAttrs_(
|
||||
XmlAttributeBase e, XmlElementBase elementid, string name, string value, int idx,
|
||||
XmlFileBase file
|
||||
) {
|
||||
xmlAttrs(e, elementid, name, value, idx, file)
|
||||
/**
|
||||
* An `XmlParent` is either an `XmlElement` or an `XmlFile`,
|
||||
* both of which can contain other elements.
|
||||
*/
|
||||
class XmlParent extends @xmlparent {
|
||||
XmlParent() {
|
||||
// explicitly restrict `this` to be either an `XmlElement` or an `XmlFile`;
|
||||
// the type `@xmlparent` currently also includes non-XML files
|
||||
this instanceof @xmlelement or xmlEncoding(this, _)
|
||||
}
|
||||
|
||||
class XmlNamespaceBase = @xmlnamespace;
|
||||
/**
|
||||
* Gets a printable representation of this XML parent.
|
||||
* (Intended to be overridden in subclasses.)
|
||||
*/
|
||||
string getName() { none() } // overridden in subclasses
|
||||
|
||||
predicate xmlNs_(XmlNamespaceBase e, string prefixName, string uri, XmlFileBase file) {
|
||||
xmlNs(e, prefixName, uri, file)
|
||||
/** Gets the file to which this XML parent belongs. */
|
||||
XmlFile getFile() { result = this or xmlElements(this, _, _, _, result) }
|
||||
|
||||
/** Gets the child element at a specified index of this XML parent. */
|
||||
XmlElement getChild(int index) { xmlElements(result, _, this, index, _) }
|
||||
|
||||
/** Gets a child element of this XML parent. */
|
||||
XmlElement getAChild() { xmlElements(result, _, this, _, _) }
|
||||
|
||||
/** Gets a child element of this XML parent with the given `name`. */
|
||||
XmlElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
|
||||
|
||||
/** Gets a comment that is a child of this XML parent. */
|
||||
XmlComment getAComment() { xmlComments(result, _, this, _) }
|
||||
|
||||
/** Gets a character sequence that is a child of this XML parent. */
|
||||
XmlCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
|
||||
|
||||
/** Gets the depth in the tree. (Overridden in XmlElement.) */
|
||||
int getDepth() { result = 0 }
|
||||
|
||||
/** Gets the number of child XML elements of this XML parent. */
|
||||
int getNumberOfChildren() { result = count(XmlElement e | xmlElements(e, _, this, _, _)) }
|
||||
|
||||
/** Gets the number of places in the body of this XML parent where text occurs. */
|
||||
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
|
||||
|
||||
/**
|
||||
* Gets the result of appending all the character sequences of this XML parent from
|
||||
* left to right, separated by a space.
|
||||
*/
|
||||
string allCharactersString() {
|
||||
result =
|
||||
concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
|
||||
}
|
||||
|
||||
predicate xmlHasNs_(XmlNamespaceableBase e, XmlNamespaceBase ns, XmlFileBase file) {
|
||||
xmlHasNs(e, ns, file)
|
||||
}
|
||||
/** Gets the text value contained in this XML parent. */
|
||||
string getTextValue() { result = this.allCharactersString() }
|
||||
|
||||
class XmlCommentBase = @xmlcomment;
|
||||
/** Gets a printable representation of this XML parent. */
|
||||
string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
predicate xmlComments_(XmlCommentBase e, string text, XmlParentBase parent, XmlFileBase file) {
|
||||
xmlComments(e, text, parent, file)
|
||||
}
|
||||
/** An XML file. */
|
||||
class XmlFile extends XmlParent, File {
|
||||
XmlFile() { xmlEncoding(this, _) }
|
||||
|
||||
class XmlCharactersBase = @xmlcharacters;
|
||||
/** Gets a printable representation of this XML file. */
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
predicate xmlChars_(
|
||||
XmlCharactersBase e, string text, XmlParentBase parent, int idx, int isCDATA, XmlFileBase file
|
||||
) {
|
||||
xmlChars(e, text, parent, idx, isCDATA, file)
|
||||
/** Gets the name of this XML file. */
|
||||
override string getName() { result = File.super.getAbsolutePath() }
|
||||
|
||||
/** Gets the encoding of this XML file. */
|
||||
string getEncoding() { xmlEncoding(this, result) }
|
||||
|
||||
/** Gets the XML file itself. */
|
||||
override XmlFile getFile() { result = this }
|
||||
|
||||
/** Gets a top-most element in an XML file. */
|
||||
XmlElement getARootElement() { result = this.getAChild() }
|
||||
|
||||
/** Gets a DTD associated with this XML file. */
|
||||
XmlDtd getADtd() { xmlDTDs(result, _, _, _, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An XML document type definition (DTD).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <!ELEMENT person (firstName, lastName?)>
|
||||
* <!ELEMENT firstName (#PCDATA)>
|
||||
* <!ELEMENT lastName (#PCDATA)>
|
||||
* ```
|
||||
*/
|
||||
class XmlDtd extends XmlLocatable, @xmldtd {
|
||||
/** Gets the name of the root element of this DTD. */
|
||||
string getRoot() { xmlDTDs(this, result, _, _, _) }
|
||||
|
||||
/** Gets the public ID of this DTD. */
|
||||
string getPublicId() { xmlDTDs(this, _, result, _, _) }
|
||||
|
||||
/** Gets the system ID of this DTD. */
|
||||
string getSystemId() { xmlDTDs(this, _, _, result, _) }
|
||||
|
||||
/** Holds if this DTD is public. */
|
||||
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }
|
||||
|
||||
/** Gets the parent of this DTD. */
|
||||
XmlParent getParent() { xmlDTDs(this, _, _, _, result) }
|
||||
|
||||
override string toString() {
|
||||
this.isPublic() and
|
||||
result = this.getRoot() + " PUBLIC '" + this.getPublicId() + "' '" + this.getSystemId() + "'"
|
||||
or
|
||||
not this.isPublic() and
|
||||
result = this.getRoot() + " SYSTEM '" + this.getSystemId() + "'"
|
||||
}
|
||||
}
|
||||
|
||||
import Make<File, Location, Input>
|
||||
/**
|
||||
* An XML element in an XML file.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
* package="com.example.exampleapp" android:versionCode="1">
|
||||
* </manifest>
|
||||
* ```
|
||||
*/
|
||||
class XmlElement extends @xmlelement, XmlParent, XmlLocatable {
|
||||
/** Holds if this XML element has the given `name`. */
|
||||
predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/** Gets the name of this XML element. */
|
||||
override string getName() { xmlElements(this, result, _, _, _) }
|
||||
|
||||
/** Gets the XML file in which this XML element occurs. */
|
||||
override XmlFile getFile() { xmlElements(this, _, _, _, result) }
|
||||
|
||||
/** Gets the parent of this XML element. */
|
||||
XmlParent getParent() { xmlElements(this, _, result, _, _) }
|
||||
|
||||
/** Gets the index of this XML element among its parent's children. */
|
||||
int getIndex() { xmlElements(this, _, _, result, _) }
|
||||
|
||||
/** Holds if this XML element has a namespace. */
|
||||
predicate hasNamespace() { xmlHasNs(this, _, _) }
|
||||
|
||||
/** Gets the namespace of this XML element, if any. */
|
||||
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }
|
||||
|
||||
/** Gets the index of this XML element among its parent's children. */
|
||||
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }
|
||||
|
||||
/** Gets the depth of this element within the XML file tree structure. */
|
||||
override int getDepth() { result = this.getParent().getDepth() + 1 }
|
||||
|
||||
/** Gets an XML attribute of this XML element. */
|
||||
XmlAttribute getAnAttribute() { result.getElement() = this }
|
||||
|
||||
/** Gets the attribute with the specified `name`, if any. */
|
||||
XmlAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
|
||||
|
||||
/** Holds if this XML element has an attribute with the specified `name`. */
|
||||
predicate hasAttribute(string name) { exists(this.getAttribute(name)) }
|
||||
|
||||
/** Gets the value of the attribute with the specified `name`, if any. */
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
/** Gets a printable representation of this XML element. */
|
||||
override string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute that occurs inside an XML element.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ```
|
||||
* package="com.example.exampleapp"
|
||||
* android:versionCode="1"
|
||||
* ```
|
||||
*/
|
||||
class XmlAttribute extends @xmlattribute, XmlLocatable {
|
||||
/** Gets the name of this attribute. */
|
||||
string getName() { xmlAttrs(this, _, result, _, _, _) }
|
||||
|
||||
/** Gets the XML element to which this attribute belongs. */
|
||||
XmlElement getElement() { xmlAttrs(this, result, _, _, _, _) }
|
||||
|
||||
/** Holds if this attribute has a namespace. */
|
||||
predicate hasNamespace() { xmlHasNs(this, _, _) }
|
||||
|
||||
/** Gets the namespace of this attribute, if any. */
|
||||
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }
|
||||
|
||||
/** Gets the value of this attribute. */
|
||||
string getValue() { xmlAttrs(this, _, _, result, _, _) }
|
||||
|
||||
/** Gets a printable representation of this XML attribute. */
|
||||
override string toString() { result = this.getName() + "=" + this.getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A namespace used in an XML file.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
* ```
|
||||
*/
|
||||
class XmlNamespace extends XmlLocatable, @xmlnamespace {
|
||||
/** Gets the prefix of this namespace. */
|
||||
string getPrefix() { xmlNs(this, result, _, _) }
|
||||
|
||||
/** Gets the URI of this namespace. */
|
||||
string getUri() { xmlNs(this, _, result, _) }
|
||||
|
||||
/** Holds if this namespace has no prefix. */
|
||||
predicate isDefault() { this.getPrefix() = "" }
|
||||
|
||||
override string toString() {
|
||||
this.isDefault() and result = this.getUri()
|
||||
or
|
||||
not this.isDefault() and result = this.getPrefix() + ":" + this.getUri()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A comment in an XML file.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <!-- This is a comment. -->
|
||||
* ```
|
||||
*/
|
||||
class XmlComment extends @xmlcomment, XmlLocatable {
|
||||
/** Gets the text content of this XML comment. */
|
||||
string getText() { xmlComments(this, result, _, _) }
|
||||
|
||||
/** Gets the parent of this XML comment. */
|
||||
XmlParent getParent() { xmlComments(this, _, result, _) }
|
||||
|
||||
/** Gets a printable representation of this XML comment. */
|
||||
override string toString() { result = this.getText() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sequence of characters that occurs between opening and
|
||||
* closing tags of an XML element, excluding other elements.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <content>This is a sequence of characters.</content>
|
||||
* ```
|
||||
*/
|
||||
class XmlCharacters extends @xmlcharacters, XmlLocatable {
|
||||
/** Gets the content of this character sequence. */
|
||||
string getCharacters() { xmlChars(this, result, _, _, _, _) }
|
||||
|
||||
/** Gets the parent of this character sequence. */
|
||||
XmlParent getParent() { xmlChars(this, _, result, _, _, _) }
|
||||
|
||||
/** Holds if this character sequence is CDATA. */
|
||||
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }
|
||||
|
||||
/** Gets a printable representation of this XML character sequence. */
|
||||
override string toString() { result = this.getCharacters() }
|
||||
}
|
||||
|
||||
@@ -1156,14 +1156,5 @@ private predicate add_eq(
|
||||
)
|
||||
}
|
||||
|
||||
private class IntegerOrPointerConstantInstruction extends ConstantInstruction {
|
||||
IntegerOrPointerConstantInstruction() {
|
||||
this instanceof IntegerConstantInstruction or
|
||||
this instanceof PointerConstantInstruction
|
||||
}
|
||||
}
|
||||
|
||||
/** The int value of integer constant expression. */
|
||||
private int int_value(Instruction i) {
|
||||
result = i.(IntegerOrPointerConstantInstruction).getValue().toInt()
|
||||
}
|
||||
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }
|
||||
|
||||
@@ -1,556 +0,0 @@
|
||||
/**
|
||||
* INTERNAL use only. This is an experimental API subject to change without notice.
|
||||
*
|
||||
* Provides classes and predicates for dealing with flow models specified in CSV format.
|
||||
*
|
||||
* The CSV specification has the following columns:
|
||||
* - Sources:
|
||||
* `namespace; type; subtypes; name; signature; ext; output; kind`
|
||||
* - Sinks:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; kind`
|
||||
* - Summaries:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; output; kind`
|
||||
*
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `namespace` column selects a namespace.
|
||||
* 2. The `type` column selects a type within that namespace.
|
||||
* 3. The `subtypes` is a boolean that indicates whether to jump to an
|
||||
* arbitrary subtype of that type. Set this to `false` if leaving the `type`
|
||||
* blank (for example, a free function).
|
||||
* 4. The `name` column optionally selects a specific named member of the type.
|
||||
* 5. The `signature` column optionally restricts the named member. If
|
||||
* `signature` is blank then no such filtering is done. The format of the
|
||||
* signature is a comma-separated list of types enclosed in parentheses. The
|
||||
* types can be short names or fully qualified names (mixing these two options
|
||||
* is not allowed within a single signature).
|
||||
* 6. The `ext` column specifies additional API-graph-like edges. Currently
|
||||
* there is only one valid value: "".
|
||||
* 7. The `input` column specifies how data enters the element selected by the
|
||||
* first 6 columns, and the `output` column specifies how data leaves the
|
||||
* element selected by the first 6 columns. An `input` can be either:
|
||||
* - "": Selects a write to the selected element in case this is a field.
|
||||
* - "Argument[n]": Selects an argument in a call to the selected element.
|
||||
* The arguments are zero-indexed, and `-1` specifies the qualifier object,
|
||||
* that is, `*this`.
|
||||
* - one or more "*" can be added in front of the argument index to indicate
|
||||
* indirection, for example, `Argument[*0]` indicates the first indirection
|
||||
* of the 0th argument.
|
||||
* - `n1..n2` syntax can be used to indicate a range of arguments, inclusive
|
||||
* at both ends. One or more "*"s can be added in front of the whole range
|
||||
* to indicate that every argument in the range is indirect, for example
|
||||
* `*0..1` is the first indirection of both arguments 0 and 1.
|
||||
* - "ReturnValue": Selects a value being returned by the selected element.
|
||||
* One or more "*" can be added as an argument to indicate indirection, for
|
||||
* example, "ReturnValue[*]" indicates the first indirection of the return
|
||||
* value.
|
||||
*
|
||||
* An `output` can be either:
|
||||
* - "": Selects a read of a selected field.
|
||||
* - "Argument[n]": Selects the post-update value of an argument in a call to
|
||||
* the selected element. That is, the value of the argument after the call
|
||||
* returns. The arguments are zero-indexed, and `-1` specifies the qualifier
|
||||
* object, that is, `*this`.
|
||||
* - one or more "*" can be added in front of the argument index to indicate
|
||||
* indirection, for example, `Argument[*0]` indicates the first indirection
|
||||
* of the 0th argument.
|
||||
* - `n1..n2` syntax can be used to indicate a range of arguments, inclusive
|
||||
* at both ends. One or more "*"s can be added in front of the whole range
|
||||
* to indicate that every argument in the range is indirect, for example
|
||||
* `*0..1` is the first indirection of both arguments 0 and 1.
|
||||
* - "Parameter[n]": Selects the value of a parameter of the selected element.
|
||||
* The syntax is the same as for "Argument", for example "Parameter[0]",
|
||||
* "Parameter[*0]", "Parameter[0..2]" etc.
|
||||
* - "ReturnValue": Selects a value being returned by the selected element.
|
||||
* One or more "*" can be added as an argument to indicate indirection, for
|
||||
* example, "ReturnValue[*]" indicates the first indirection of the return
|
||||
* value.
|
||||
* 8. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources "remote" indicates a default remote flow source, and for summaries
|
||||
* "taint" indicates a default additional taint step and "value" indicates a
|
||||
* globally applicable value-preserving step.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import new.DataFlow
|
||||
private import internal.FlowSummaryImpl
|
||||
private import internal.FlowSummaryImpl::Public
|
||||
private import internal.FlowSummaryImpl::Private
|
||||
private import internal.FlowSummaryImpl::Private::External
|
||||
private import codeql.mad.ModelValidation as SharedModelVal
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* A unit class for adding additional source model rows.
|
||||
*
|
||||
* Extend this class to add additional source definitions.
|
||||
*/
|
||||
class SourceModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a source definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional sink model rows.
|
||||
*
|
||||
* Extend this class to add additional sink definitions.
|
||||
*/
|
||||
class SinkModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a sink definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional summary model rows.
|
||||
*
|
||||
* Extend this class to add additional flow summary definitions.
|
||||
*/
|
||||
class SummaryModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a summary definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/** Holds if `row` is a source model. */
|
||||
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is a sink model. */
|
||||
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is a summary model. */
|
||||
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
|
||||
) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = output and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
/** 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
|
||||
) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
summaryModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = output and
|
||||
row.splitAt(";", 8) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
private predicate relevantNamespace(string namespace) {
|
||||
sourceModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
summaryModel(namespace, _, _, _, _, _, _, _, _, _)
|
||||
}
|
||||
|
||||
private predicate namespaceLink(string shortns, string longns) {
|
||||
relevantNamespace(shortns) and
|
||||
relevantNamespace(longns) and
|
||||
longns.prefix(longns.indexOf("::")) = shortns
|
||||
}
|
||||
|
||||
private predicate canonicalNamespace(string namespace) {
|
||||
relevantNamespace(namespace) and not namespaceLink(_, namespace)
|
||||
}
|
||||
|
||||
private predicate canonicalNamespaceLink(string namespace, string subns) {
|
||||
canonicalNamespace(namespace) and
|
||||
(subns = namespace or namespaceLink(namespace, subns))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if CSV framework coverage of `namespace` is `n` api endpoints of the
|
||||
* kind `(kind, part)`.
|
||||
*/
|
||||
predicate modelCoverage(string namespace, int namespaces, string kind, string part, int n) {
|
||||
namespaces = strictcount(string subns | canonicalNamespaceLink(namespace, subns)) and
|
||||
(
|
||||
part = "source" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
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 |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
n =
|
||||
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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides a query predicate to check the CSV data for validation errors. */
|
||||
module CsvValidation {
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, AccessPath input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
invalidSpecComponent(input, part) and
|
||||
not part = "" and
|
||||
not (part = "Argument" and pred = "sink") and
|
||||
not parseArg(part, _)
|
||||
or
|
||||
part = input.getToken(_) and
|
||||
parseParam(part, _)
|
||||
) and
|
||||
result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelOutput() {
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _, _) and pred = "summary"
|
||||
|
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") and
|
||||
result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
|
||||
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) }
|
||||
|
||||
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) }
|
||||
|
||||
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) }
|
||||
}
|
||||
|
||||
private module KindVal = SharedModelVal::KindValidation<KindValConfig>;
|
||||
|
||||
private string getInvalidModelSubtype() {
|
||||
exists(string pred, string row |
|
||||
sourceModel(row) and pred = "source"
|
||||
or
|
||||
sinkModel(row) and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and pred = "summary"
|
||||
|
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelColumnCount() {
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 8 and pred = "source"
|
||||
or
|
||||
sinkModel(row) and expect = 8 and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and expect = 9 and pred = "summary"
|
||||
|
|
||||
exists(int cols |
|
||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||
cols != expect and
|
||||
result =
|
||||
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
|
||||
"."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
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"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
|
||||
|
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
|
||||
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and
|
||||
result = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_<>,]*") and
|
||||
result = "Dubious member name \"" + name + "\" in " + pred + " model."
|
||||
or
|
||||
not signature.regexpMatch("|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)") and
|
||||
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
||||
or
|
||||
not ext.regexpMatch("|Attribute") and
|
||||
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
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, _, _, _, _)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function c, int i) {
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
exists(int n, string p | c.getParameter(n).getType().toString() = p |
|
||||
i = 2 * n and result = p
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the element in module `namespace` that satisfies the following properties:
|
||||
* 1. If the element is a member of a class-like type, then the class-like type has name `type`
|
||||
* 2. If `subtypes = true` and the element is a member of a class-like type, then overrides of the element
|
||||
* are also returned.
|
||||
* 3. The element has name `name`
|
||||
* 4. If `signature` is non-empty, then the element has a list of parameter types described by `signature`.
|
||||
*
|
||||
* NOTE: `namespace` is currently not used (since we don't properly extract modules yet).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private Element interpretElement0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
(
|
||||
// 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
|
||||
exists(Class namedClass, Class classWithMethod, Function method |
|
||||
classWithMethod = method.getClassAndName(name) and
|
||||
namedClass.hasQualifiedName(namespace, type) and
|
||||
matchesSignature(method, signature) and
|
||||
result = method
|
||||
|
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
classWithMethod = namedClass.getADerivedClass*()
|
||||
or
|
||||
// member declared directly in the named type
|
||||
subtypes = false and
|
||||
classWithMethod = namedClass
|
||||
)
|
||||
or
|
||||
// Member variables
|
||||
signature = "" and
|
||||
exists(Class namedClass, Class classWithMember, MemberVariable member |
|
||||
member.getName() = name and
|
||||
member = classWithMember.getAMember() and
|
||||
namedClass.hasQualifiedName(namespace, type) and
|
||||
result = member
|
||||
|
|
||||
// field declared in the named type or a subtype of it (or an extension of any)
|
||||
subtypes = true and
|
||||
classWithMember = namedClass.getADerivedClass*()
|
||||
or
|
||||
// field declared directly in the named type (or an extension of it)
|
||||
subtypes = false and
|
||||
classWithMember = namedClass
|
||||
)
|
||||
or
|
||||
// Global or namespace variables
|
||||
signature = "" and
|
||||
type = "" and
|
||||
subtypes = false and
|
||||
result = any(GlobalOrNamespaceVariable v | v.hasQualifiedName(namespace, name))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
||||
Element interpretElement(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, ext) and
|
||||
exists(Element e | e = interpretElement0(namespace, type, subtypes, name, signature) |
|
||||
ext = "" and result = e
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(DataFlow::Node node, string kind) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSourceNode(n, kind, _) and n.asNode() = node // TODO
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(DataFlow::Node node, string kind) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSinkNode(n, kind, _) and n.asNode() = node // TODO
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
private predicate interpretSummary(
|
||||
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) 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, _, _, _, _) }
|
||||
|
||||
private predicate relevantSummaryElementManual(string input, string output, string kind) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
provenance.isManual()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
provenance.isGenerated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(
|
||||
string input, string output, boolean preservesValue, string model
|
||||
) {
|
||||
exists(string kind |
|
||||
this.relevantSummaryElementManual(input, output, kind)
|
||||
or
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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_ }
|
||||
}
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -286,12 +286,6 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
predicate knownSourceModel(Node source, string model) { none() }
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { none() }
|
||||
|
||||
class DataFlowSecondLevelScope = Unit;
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -516,7 +516,7 @@ private module ThisFlow {
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo, _)
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Field flow is not strictly a "step" but covers the whole function
|
||||
// transitively. There's no way to get a step-like relation out of the global
|
||||
@@ -530,67 +530,64 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
* This is the local flow predicate that's used as a building block in global
|
||||
* data flow. It may have less flow than the `localFlowStep` predicate.
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
(
|
||||
// Expr -> Expr
|
||||
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
// Assignment -> LValue post-update node
|
||||
//
|
||||
// This is used for assignments whose left-hand side is not a variable
|
||||
// assignment or a storeStep but is still modeled by other means. It could be
|
||||
// a call to `operator*` or `operator[]` where taint should flow to the
|
||||
// post-update node of the qualifier.
|
||||
exists(AssignExpr assign |
|
||||
nodeFrom.asExpr() = assign and
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
|
||||
)
|
||||
or
|
||||
// Node -> FlowVar -> VariableAccess
|
||||
exists(FlowVar var |
|
||||
(
|
||||
exprToVarStep(nodeFrom.asExpr(), var)
|
||||
or
|
||||
varSourceBaseCase(var, nodeFrom.asParameter())
|
||||
or
|
||||
varSourceBaseCase(var, nodeFrom.asUninitialized())
|
||||
or
|
||||
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
|
||||
) and
|
||||
varToNodeStep(var, nodeTo)
|
||||
)
|
||||
or
|
||||
// Expr -> DefinitionByReferenceNode
|
||||
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
|
||||
or
|
||||
// `this` -> adjacent-`this`
|
||||
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
|
||||
or
|
||||
// post-update-`this` -> following-`this`-ref
|
||||
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
|
||||
or
|
||||
// In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
|
||||
// from which there is field flow to `x` via reverse read.
|
||||
exists(PartialDefinition def, Expr inner, Expr outer |
|
||||
def.definesExpressions(inner, outer) and
|
||||
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
|
||||
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
|
||||
)
|
||||
or
|
||||
// Reverse flow: data that flows from the post-update node of a reference
|
||||
// returned by a function call, back into the qualifier of that function.
|
||||
// This allows data to flow 'in' through references returned by a modeled
|
||||
// function such as `operator[]`.
|
||||
exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
|
||||
call.getTarget() = f and
|
||||
inModel.isReturnValueDeref() and
|
||||
outModel.isQualifierObject() and
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
|
||||
nodeTo.asDefiningArgument() = call.getQualifier()
|
||||
)
|
||||
) and
|
||||
model = ""
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Expr -> Expr
|
||||
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
// Assignment -> LValue post-update node
|
||||
//
|
||||
// This is used for assignments whose left-hand side is not a variable
|
||||
// assignment or a storeStep but is still modeled by other means. It could be
|
||||
// a call to `operator*` or `operator[]` where taint should flow to the
|
||||
// post-update node of the qualifier.
|
||||
exists(AssignExpr assign |
|
||||
nodeFrom.asExpr() = assign and
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
|
||||
)
|
||||
or
|
||||
// Node -> FlowVar -> VariableAccess
|
||||
exists(FlowVar var |
|
||||
(
|
||||
exprToVarStep(nodeFrom.asExpr(), var)
|
||||
or
|
||||
varSourceBaseCase(var, nodeFrom.asParameter())
|
||||
or
|
||||
varSourceBaseCase(var, nodeFrom.asUninitialized())
|
||||
or
|
||||
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
|
||||
) and
|
||||
varToNodeStep(var, nodeTo)
|
||||
)
|
||||
or
|
||||
// Expr -> DefinitionByReferenceNode
|
||||
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
|
||||
or
|
||||
// `this` -> adjacent-`this`
|
||||
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
|
||||
or
|
||||
// post-update-`this` -> following-`this`-ref
|
||||
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
|
||||
or
|
||||
// In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
|
||||
// from which there is field flow to `x` via reverse read.
|
||||
exists(PartialDefinition def, Expr inner, Expr outer |
|
||||
def.definesExpressions(inner, outer) and
|
||||
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
|
||||
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
|
||||
)
|
||||
or
|
||||
// Reverse flow: data that flows from the post-update node of a reference
|
||||
// returned by a function call, back into the qualifier of that function.
|
||||
// This allows data to flow 'in' through references returned by a modeled
|
||||
// function such as `operator[]`.
|
||||
exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
|
||||
call.getTarget() = f and
|
||||
inModel.isReturnValueDeref() and
|
||||
outModel.isQualifierObject() and
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
|
||||
nodeTo.asDefiningArgument() = call.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
/**
|
||||
* Provides classes and predicates for defining flow summaries.
|
||||
*/
|
||||
|
||||
private import cpp as Cpp
|
||||
private import codeql.dataflow.internal.FlowSummaryImpl
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific as DataFlowImplSpecific
|
||||
private import semmle.code.cpp.dataflow.ExternalFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
class SummarizedCallableBase = Function;
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result = TDirectPosition(-1) }
|
||||
|
||||
ReturnKind getStandardReturnValueKind() { result.(NormalReturnKind).getIndirectionIndex() = 0 }
|
||||
|
||||
string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() }
|
||||
|
||||
string encodeArgumentPosition(ArgumentPosition pos) { result = pos.toString() }
|
||||
|
||||
string encodeReturn(ReturnKind rk, string arg) {
|
||||
rk != getStandardReturnValueKind() and
|
||||
result = "ReturnValue" and
|
||||
arg = repeatStars(rk.(NormalReturnKind).getIndirectionIndex())
|
||||
}
|
||||
|
||||
string encodeContent(ContentSet cs, string arg) {
|
||||
exists(FieldContent c |
|
||||
cs.isSingleton(c) and
|
||||
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
|
||||
result = "Field" and
|
||||
arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName()
|
||||
)
|
||||
}
|
||||
|
||||
string encodeWithoutContent(ContentSet c, string arg) {
|
||||
// used for type tracking, not currently used in C/C++.
|
||||
result = "WithoutContent" + c and arg = ""
|
||||
}
|
||||
|
||||
string encodeWithContent(ContentSet c, string arg) {
|
||||
// used for type tracking, not currently used in C/C++.
|
||||
result = "WithContent" + c and arg = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an argument / parameter position string, for example the `0` in `Argument[0]`.
|
||||
* Supports ranges (`Argument[x..y]`), qualifiers (`Argument[-1]`), indirections
|
||||
* (`Argument[*x]`) and combinations (such as `Argument[**0..1]`).
|
||||
*/
|
||||
bindingset[argString]
|
||||
private TPosition decodePosition(string argString) {
|
||||
exists(int indirection, string posString, int pos |
|
||||
argString = repeatStars(indirection) + posString and
|
||||
pos = AccessPath::parseInt(posString) and
|
||||
(
|
||||
pos >= 0 and indirection = 0 and result = TDirectPosition(pos)
|
||||
or
|
||||
pos >= 0 and indirection > 0 and result = TIndirectionPosition(pos, indirection)
|
||||
or
|
||||
// `Argument[-1]` / `Parameter[-1]` is the qualifier object `*this`, not the `this` pointer itself.
|
||||
pos = -1 and result = TIndirectionPosition(pos, indirection + 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[token]
|
||||
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
|
||||
token.getName() = "Argument" and
|
||||
result = decodePosition(token.getAnArgument())
|
||||
}
|
||||
|
||||
bindingset[token]
|
||||
ArgumentPosition decodeUnknownArgumentPosition(AccessPath::AccessPathTokenBase token) {
|
||||
token.getName() = "Parameter" and
|
||||
result = decodePosition(token.getAnArgument())
|
||||
}
|
||||
|
||||
bindingset[token]
|
||||
ContentSet decodeUnknownContent(AccessPath::AccessPathTokenBase token) {
|
||||
// field content (no indirection support)
|
||||
exists(FieldContent c |
|
||||
result.isSingleton(c) and
|
||||
token.getName() = c.getField().getName() and
|
||||
not exists(token.getArgumentList()) and
|
||||
c.getIndirectionIndex() = 1
|
||||
)
|
||||
or
|
||||
// field content (with indirection support)
|
||||
exists(FieldContent c |
|
||||
result.isSingleton(c) and
|
||||
token.getName() = c.getField().getName() and
|
||||
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
|
||||
token.getAnArgument() = repeatStars(c.getIndirectionIndex() - 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Make<Location, DataFlowImplSpecific::CppDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
result.getStaticCallTarget().getUnderlyingCallable() = sc
|
||||
}
|
||||
}
|
||||
|
||||
module SourceSinkInterpretationInput implements
|
||||
Impl::Private::External::SourceSinkInterpretationInputSig
|
||||
{
|
||||
class Element = Cpp::Element;
|
||||
|
||||
class SourceOrSinkElement = Element;
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output`, kind `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate sourceElement(
|
||||
SourceOrSinkElement e, string output, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext) and
|
||||
model = "" // TODO
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `e` with input specification
|
||||
* `input`, kind `kind` and provenance `provenance`.
|
||||
*/
|
||||
predicate sinkElement(
|
||||
SourceOrSinkElement e, string input, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
|
||||
e = interpretElement(package, type, subtypes, name, signature, ext) and
|
||||
model = "" // TODO
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TInterpretNode =
|
||||
TElement_(Element n) or
|
||||
TNode_(Node n)
|
||||
|
||||
/** An entity used to interpret a source/sink specification. */
|
||||
class InterpretNode extends TInterpretNode {
|
||||
/** Gets the element that this node corresponds to, if any. */
|
||||
SourceOrSinkElement asElement() { this = TElement_(result) }
|
||||
|
||||
/** Gets the data-flow node that this node corresponds to, if any. */
|
||||
Node asNode() { this = TNode_(result) }
|
||||
|
||||
/** Gets the call that this node corresponds to, if any. */
|
||||
DataFlowCall asCall() {
|
||||
this.asElement() = result.asCallInstruction().getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/** Gets the callable that this node corresponds to, if any. */
|
||||
DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() }
|
||||
|
||||
/** Gets the target of this call, if any. */
|
||||
Element getCallTarget() { result = this.asCall().getStaticCallTarget().getUnderlyingCallable() }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() {
|
||||
result = this.asElement().toString()
|
||||
or
|
||||
result = this.asNode().toString()
|
||||
or
|
||||
result = this.asCall().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation() {
|
||||
result = this.asElement().getLocation()
|
||||
or
|
||||
result = this.asNode().getLocation()
|
||||
or
|
||||
result = this.asCall().getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides additional sink specification logic. */
|
||||
bindingset[c]
|
||||
predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) {
|
||||
// Allow variables to be picked as output nodes.
|
||||
exists(Node n, Element ast |
|
||||
n = node.asNode() and
|
||||
ast = mid.asElement()
|
||||
|
|
||||
c = "" and
|
||||
n.asExpr().(VariableAccess).getTarget() = ast
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides additional source specification logic. */
|
||||
bindingset[c]
|
||||
predicate interpretInput(string c, InterpretNode mid, InterpretNode node) {
|
||||
exists(Node n, Element ast, VariableAccess e |
|
||||
n = node.asNode() and
|
||||
ast = mid.asElement() and
|
||||
e.getTarget() = ast
|
||||
|
|
||||
// Allow variables to be picked as input nodes.
|
||||
// We could simply do this as `e = n.asExpr()`, but that would not allow
|
||||
// us to pick `x` as a sink in an example such as `x = source()` (but
|
||||
// only subsequent uses of `x`) since the variable access on `x` doesn't
|
||||
// actually load the value of `x`. So instead, we pick the instruction
|
||||
// node corresponding to the generated `StoreInstruction` and use the
|
||||
// expression associated with the destination instruction. This means
|
||||
// that the `x` in `x = source()` can be marked as an input.
|
||||
c = "" and
|
||||
exists(StoreInstruction store |
|
||||
store.getDestinationAddress().getUnconvertedResultExpression() = e and
|
||||
n.asInstruction() = store
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Private {
|
||||
import Impl::Private
|
||||
|
||||
module Steps = Impl::Private::Steps<StepsInput>;
|
||||
|
||||
module External {
|
||||
import Impl::Private::External
|
||||
import Impl::Private::External::SourceSinkInterpretation<SourceSinkInterpretationInput>
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides predicates for constructing summary components.
|
||||
*/
|
||||
module SummaryComponent {
|
||||
private import Impl::Private::SummaryComponent as SC
|
||||
|
||||
predicate parameter = SC::parameter/1;
|
||||
|
||||
predicate argument = SC::argument/1;
|
||||
|
||||
predicate content = SC::content/1;
|
||||
|
||||
predicate withoutContent = SC::withoutContent/1;
|
||||
|
||||
predicate withContent = SC::withContent/1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides predicates for constructing stacks of summary components.
|
||||
*/
|
||||
module SummaryComponentStack {
|
||||
private import Impl::Private::SummaryComponentStack as SCS
|
||||
|
||||
predicate singleton = SCS::singleton/1;
|
||||
|
||||
predicate push = SCS::push/2;
|
||||
|
||||
predicate argument = SCS::argument/1;
|
||||
}
|
||||
}
|
||||
|
||||
module Public = Impl::Public;
|
||||
@@ -32,8 +32,8 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
* Holds if the additional step from `src` to `sink` should be included in all
|
||||
* global taint flow configurations.
|
||||
*/
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink, string model) {
|
||||
localAdditionalTaintStep(src, sink) and model = ""
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintStep(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2, _)
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2, _)
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,12 +63,6 @@ class Expr extends StmtParent, @expr {
|
||||
* order of destruction.
|
||||
*/
|
||||
DestructorCall getImplicitDestructorCall(int n) {
|
||||
exists(Expr e |
|
||||
e = this.(TemporaryObjectExpr).getExpr() and
|
||||
synthetic_destructor_call(e, max(int i | synthetic_destructor_call(e, i, _)) - n, result)
|
||||
)
|
||||
or
|
||||
not this = any(TemporaryObjectExpr temp).getExpr() and
|
||||
synthetic_destructor_call(this, max(int i | synthetic_destructor_call(this, i, _)) - n, result)
|
||||
}
|
||||
|
||||
@@ -1021,33 +1015,8 @@ class DeleteOrDeleteArrayExpr extends Expr, TDeleteOrDeleteArrayExpr {
|
||||
Expr getExpr() {
|
||||
// If there is a destructor call, the object being deleted is the qualifier
|
||||
// otherwise it is the third child.
|
||||
exists(Expr exprWithReuse | exprWithReuse = this.getExprWithReuse() |
|
||||
if not exprWithReuse instanceof ReuseExpr
|
||||
then result = exprWithReuse
|
||||
else result = this.getDestructorCall().getQualifier()
|
||||
)
|
||||
result = this.getChild(3) or result = this.getDestructorCall().getQualifier()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object or array being deleted, and gets a `ReuseExpr` when there
|
||||
* is a destructor call and the object is also the qualifier of the call.
|
||||
*
|
||||
* For example, given:
|
||||
* ```
|
||||
* struct HasDestructor { ~HasDestructor(); };
|
||||
* struct PlainOldData { int x, char y; };
|
||||
*
|
||||
* void f(HasDestructor* hasDestructor, PlainOldData* pod) {
|
||||
* delete hasDestructor;
|
||||
* delete pod;
|
||||
* }
|
||||
* ```
|
||||
* This predicate yields a `ReuseExpr` for `delete hasDestructor`, as the
|
||||
* the deleted expression has a destructor, and that expression is also
|
||||
* the qualifier of the destructor call. In the case of `delete pod` the
|
||||
* predicate does not yield a `ReuseExpr`, as there is no destructor call.
|
||||
*/
|
||||
Expr getExprWithReuse() { result = this.getChild(3) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1338,24 +1307,6 @@ class CoAwaitExpr extends UnaryOperation, @co_await {
|
||||
override string getOperator() { result = "co_await" }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
/**
|
||||
* Gets the Boolean expression that is used to decide if the enclosing
|
||||
* coroutine should be suspended.
|
||||
*/
|
||||
Expr getAwaitReady() { result = this.getChild(1) }
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the resume point if the enclosing
|
||||
* coroutine was suspended.
|
||||
*/
|
||||
Expr getAwaitResume() { result = this.getChild(2) }
|
||||
|
||||
/**
|
||||
* Gets the expression that is evaluated when the enclosing coroutine is
|
||||
* suspended.
|
||||
*/
|
||||
Expr getAwaitSuspend() { result = this.getChild(3) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1370,24 +1321,6 @@ class CoYieldExpr extends UnaryOperation, @co_yield {
|
||||
override string getOperator() { result = "co_yield" }
|
||||
|
||||
override int getPrecedence() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets the Boolean expression that is used to decide if the enclosing
|
||||
* coroutine should be suspended.
|
||||
*/
|
||||
Expr getAwaitReady() { result = this.getChild(1) }
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the resume point if the enclosing
|
||||
* coroutine was suspended.
|
||||
*/
|
||||
Expr getAwaitResume() { result = this.getChild(2) }
|
||||
|
||||
/**
|
||||
* Gets the expression that is evaluated when the enclosing coroutine is
|
||||
* suspended.
|
||||
*/
|
||||
Expr getAwaitSuspend() { result = this.getChild(3) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,6 @@ import cpp
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import internal.DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Resolve potential target function(s) for `call`.
|
||||
@@ -17,9 +16,8 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
* to identify the possible target(s).
|
||||
*/
|
||||
Function resolveCall(Call call) {
|
||||
exists(DataFlowCall dataFlowCall, CallInstruction callInstruction |
|
||||
exists(CallInstruction callInstruction |
|
||||
callInstruction.getAst() = call and
|
||||
callInstruction = dataFlowCall.asCallInstruction() and
|
||||
result = viableCallable(dataFlowCall).getUnderlyingCallable()
|
||||
result = viableCallable(callInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ DataFlowCallable defaultViableCallable(DataFlowCall call) {
|
||||
// function with the right signature is present in the database, we return
|
||||
// that as a potential callee.
|
||||
exists(string qualifiedName, int nparams |
|
||||
callSignatureWithoutBody(qualifiedName, nparams, call.asCallInstruction()) and
|
||||
functionSignatureWithBody(qualifiedName, nparams, result.getUnderlyingCallable()) and
|
||||
callSignatureWithoutBody(qualifiedName, nparams, call) and
|
||||
functionSignatureWithBody(qualifiedName, nparams, result) and
|
||||
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
|
||||
)
|
||||
or
|
||||
// Virtual dispatch
|
||||
result.asSourceCallable() = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,9 +40,7 @@ DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
result = defaultViableCallable(call)
|
||||
or
|
||||
// Additional call targets
|
||||
result.getUnderlyingCallable() =
|
||||
any(AdditionalCallTarget additional)
|
||||
.viableTarget(call.asCallInstruction().getUnconvertedResultExpression())
|
||||
result = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,7 +150,7 @@ private module VirtualDispatch {
|
||||
ReturnNode node, ReturnKind kind, DataFlowCallable callable
|
||||
) {
|
||||
node.getKind() = kind and
|
||||
node.getEnclosingCallable() = callable.getUnderlyingCallable()
|
||||
node.getEnclosingCallable() = callable
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
@@ -178,15 +176,10 @@ private module VirtualDispatch {
|
||||
/** Call to a virtual function. */
|
||||
private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(
|
||||
this.getStaticCallTarget()
|
||||
.getUnderlyingCallable()
|
||||
.(VirtualFunction)
|
||||
.getAnOverridingFunction()
|
||||
)
|
||||
exists(this.getStaticCallTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
}
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getArgument(-1) }
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getThisArgument() }
|
||||
|
||||
override MemberFunction resolve() {
|
||||
exists(Class overridingClass |
|
||||
@@ -201,8 +194,7 @@ private module VirtualDispatch {
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) {
|
||||
overridingFunction.getAnOverriddenFunction+() =
|
||||
this.getStaticCallTarget().getUnderlyingCallable().(VirtualFunction) and
|
||||
overridingFunction.getAnOverriddenFunction+() = this.getStaticCallTarget().(VirtualFunction) and
|
||||
overridingFunction.getDeclaringType() = overridingClass
|
||||
}
|
||||
|
||||
@@ -264,12 +256,12 @@ predicate mayBenefitFromCallContext(DataFlowCall call) { mayBenefitFromCallConte
|
||||
* value is given as the `arg`'th argument to `f`.
|
||||
*/
|
||||
private predicate mayBenefitFromCallContext(
|
||||
VirtualDispatch::DataSensitiveCall call, DataFlowCallable f, int arg
|
||||
VirtualDispatch::DataSensitiveCall call, Function f, int arg
|
||||
) {
|
||||
f = pragma[only_bind_out](call).getEnclosingCallable() and
|
||||
exists(InitializeParameterInstruction init |
|
||||
not exists(call.getStaticCallTarget()) and
|
||||
init.getEnclosingFunction() = f.getUnderlyingCallable() and
|
||||
init.getEnclosingFunction() = f and
|
||||
call.flowsFrom(DataFlow::instructionNode(init), _) and
|
||||
init.getParameter().getIndex() = arg
|
||||
)
|
||||
@@ -281,11 +273,10 @@ private predicate mayBenefitFromCallContext(
|
||||
*/
|
||||
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableCallable(call) and
|
||||
exists(int i, DataFlowCallable f |
|
||||
exists(int i, Function f |
|
||||
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
|
||||
f = ctx.getStaticCallTarget() and
|
||||
result.asSourceCallable() =
|
||||
ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
|
||||
result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2) and
|
||||
model = ""
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
|
||||
@@ -22,13 +22,9 @@ module CppDataFlow implements InputSig<Location> {
|
||||
|
||||
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
|
||||
|
||||
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
|
||||
|
||||
predicate validParameterAliasStep = Private::validParameterAliasStep/2;
|
||||
|
||||
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
|
||||
|
||||
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
|
||||
|
||||
predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ private import DataFlowUtil
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import SsaInternals as Ssa
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import codeql.util.Unit
|
||||
@@ -78,8 +77,15 @@ module NodeStars {
|
||||
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
|
||||
or
|
||||
result = n.(FinalParameterNode).getIndirectionIndex()
|
||||
}
|
||||
|
||||
private int maxNumberOfIndirections() { result = max(getNumberOfIndirections(_)) }
|
||||
|
||||
private string repeatStars(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
result = n.(BodyLessParameterNodeImpl).getIndirectionIndex()
|
||||
n = [1 .. maxNumberOfIndirections()] and
|
||||
result = "*" + repeatStars(n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,11 +97,6 @@ module NodeStars {
|
||||
|
||||
import NodeStars
|
||||
|
||||
/**
|
||||
* A cut-down `DataFlow::Node` class that does not depend on the output of SSA.
|
||||
* This can thus be safely used in the SSA computations themselves, as well as
|
||||
* in construction of other node classes (`TIRDataFlowNode`).
|
||||
*/
|
||||
class Node0Impl extends TIRDataFlowNode0 {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -332,9 +333,7 @@ private module IndirectInstructions {
|
||||
import IndirectInstructions
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) {
|
||||
result.getUnderlyingCallable() = n.getEnclosingCallable()
|
||||
}
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
|
||||
@@ -380,30 +379,12 @@ private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode
|
||||
override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
|
||||
exists(int indirectionIndex |
|
||||
pos = TIndirectionPosition(argumentIndex, pragma[only_bind_into](indirectionIndex)) and
|
||||
this.getCallInstruction() = dfCall.asCallInstruction() and
|
||||
this.getCallInstruction() = dfCall and
|
||||
super.hasAddressOperandAndIndirectionIndex(_, pragma[only_bind_into](indirectionIndex))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument node that is part of a summary. These only occur when the
|
||||
* summary contains a synthesized call.
|
||||
*/
|
||||
class SummaryArgumentNode extends ArgumentNode, FlowSummaryNode {
|
||||
private SummaryCall call_;
|
||||
private ArgumentPosition pos_;
|
||||
|
||||
SummaryArgumentNode() {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(call_.getReceiver(), this.getSummaryNode(), pos_)
|
||||
}
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
call = call_ and
|
||||
pos = pos_
|
||||
}
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition = Position;
|
||||
|
||||
@@ -451,42 +432,24 @@ class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
}
|
||||
|
||||
newtype TPosition =
|
||||
TDirectPosition(int argumentIndex) { exists(any(CallInstruction c).getArgument(argumentIndex)) } or
|
||||
TDirectPosition(int index) { exists(any(CallInstruction c).getArgument(index)) } or
|
||||
TIndirectionPosition(int argumentIndex, int indirectionIndex) {
|
||||
Ssa::hasIndirectOperand(any(CallInstruction call).getArgumentOperand(argumentIndex),
|
||||
hasOperandAndIndex(_, any(CallInstruction call).getArgumentOperand(argumentIndex),
|
||||
indirectionIndex)
|
||||
}
|
||||
|
||||
private newtype TReturnKind =
|
||||
TNormalReturnKind(int indirectionIndex) {
|
||||
// derive a possible return indirection from SSA
|
||||
// (this is a more durable approach if SSA infers additional indirections for any reason)
|
||||
Ssa::hasIndirectOperand(any(ReturnValueInstruction ret).getReturnAddressOperand(),
|
||||
indirectionIndex + 1) // We subtract one because the return loads the value.
|
||||
or
|
||||
// derive a possible return kind from the AST
|
||||
// (this approach includes functions declared that have no body; they may still have flow summaries)
|
||||
indirectionIndex =
|
||||
[0 .. max(Cpp::Function f |
|
||||
not exists(f.getBlock())
|
||||
|
|
||||
Ssa::getMaxIndirectionsForType(f.getUnspecifiedType()) - 1 // -1 because a returned value is a prvalue not a glvalue
|
||||
)]
|
||||
TNormalReturnKind(int index) {
|
||||
exists(IndirectReturnNode return |
|
||||
return.isNormalReturn() and
|
||||
index = return.getIndirectionIndex() - 1 // We subtract one because the return loads the value.
|
||||
)
|
||||
} or
|
||||
TIndirectReturnKind(int argumentIndex, int indirectionIndex) {
|
||||
// derive a possible return argument from SSA
|
||||
exists(Ssa::FinalParameterUse use |
|
||||
use.getIndirectionIndex() = indirectionIndex and
|
||||
use.getArgumentIndex() = argumentIndex
|
||||
exists(IndirectReturnNode return |
|
||||
return.isParameterReturn(argumentIndex) and
|
||||
indirectionIndex = return.getIndirectionIndex()
|
||||
)
|
||||
or
|
||||
// derive a possible return argument from the AST
|
||||
indirectionIndex =
|
||||
[0 .. max(Cpp::Function f |
|
||||
not exists(f.getBlock())
|
||||
|
|
||||
Ssa::getMaxIndirectionsForType(f.getParameter(argumentIndex).getUnspecifiedType()) - 1 // -1 because an argument is a prvalue not a glvalue
|
||||
)]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -494,44 +457,29 @@ private newtype TReturnKind =
|
||||
* from a callable. For C++, this is simply a function return.
|
||||
*/
|
||||
class ReturnKind extends TReturnKind {
|
||||
/**
|
||||
* Gets the indirection index of this return kind.
|
||||
*/
|
||||
abstract int getIndirectionIndex();
|
||||
|
||||
/** Gets a textual representation of this return kind. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A value returned from a callable using a `return` statement, that is, a "normal" return.
|
||||
*/
|
||||
class NormalReturnKind extends ReturnKind, TNormalReturnKind {
|
||||
int indirectionIndex;
|
||||
private class NormalReturnKind extends ReturnKind, TNormalReturnKind {
|
||||
int index;
|
||||
|
||||
NormalReturnKind() { this = TNormalReturnKind(indirectionIndex) }
|
||||
|
||||
override int getIndirectionIndex() { result = indirectionIndex }
|
||||
NormalReturnKind() { this = TNormalReturnKind(index) }
|
||||
|
||||
override string toString() { result = "indirect return" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A value returned from a callable through a parameter.
|
||||
*/
|
||||
private class IndirectReturnKind extends ReturnKind, TIndirectReturnKind {
|
||||
int argumentIndex;
|
||||
int indirectionIndex;
|
||||
|
||||
IndirectReturnKind() { this = TIndirectReturnKind(argumentIndex, indirectionIndex) }
|
||||
|
||||
override int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override string toString() { result = "indirect outparam[" + argumentIndex.toString() + "]" }
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
abstract class ReturnNode extends Node {
|
||||
class ReturnNode extends Node instanceof IndirectReturnNode {
|
||||
/** Gets the kind of this returned value. */
|
||||
abstract ReturnKind getKind();
|
||||
}
|
||||
@@ -562,17 +510,6 @@ class ReturnIndirectionNode extends IndirectReturnNode, ReturnNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return node that is part of a summary.
|
||||
*/
|
||||
private class SummaryReturnNode extends ReturnNode, FlowSummaryNode {
|
||||
private ReturnKind rk;
|
||||
|
||||
SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), rk) }
|
||||
|
||||
override ReturnKind getKind() { result = rk }
|
||||
}
|
||||
|
||||
private Operand fullyConvertedCallStepImpl(Operand op) {
|
||||
not exists(getANonConversionUse(op)) and
|
||||
exists(Instruction instr |
|
||||
@@ -679,10 +616,7 @@ predicate simpleOutNode(Node node, CallInstruction call) {
|
||||
instructionForFullyConvertedCall(node.asInstruction(), call)
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that represents the output of a call (for example, a
|
||||
* return value) at the call site.
|
||||
*/
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends Node {
|
||||
OutNode() {
|
||||
// Return values not hidden behind indirections
|
||||
@@ -693,15 +627,11 @@ class OutNode extends Node {
|
||||
or
|
||||
// Modified arguments hidden behind indirections
|
||||
this instanceof IndirectArgumentOutNode
|
||||
or
|
||||
// Summary node
|
||||
FlowSummaryImpl::Private::summaryOutNode(_, this.(FlowSummaryNode).getSummaryNode(), _)
|
||||
}
|
||||
|
||||
/** Gets the underlying call. */
|
||||
abstract DataFlowCall getCall();
|
||||
|
||||
/** Gets the kind of this out node. */
|
||||
abstract ReturnKind getReturnKind();
|
||||
}
|
||||
|
||||
@@ -710,44 +640,25 @@ private class DirectCallOutNode extends OutNode {
|
||||
|
||||
DirectCallOutNode() { simpleOutNode(this, call) }
|
||||
|
||||
override DataFlowCall getCall() { result.asCallInstruction() = call }
|
||||
override DataFlowCall getCall() { result = call }
|
||||
|
||||
override ReturnKind getReturnKind() { result = TNormalReturnKind(0) }
|
||||
}
|
||||
|
||||
private class IndirectCallOutNode extends OutNode, IndirectReturnOutNode {
|
||||
override DataFlowCall getCall() { result.asCallInstruction() = this.getCallInstruction() }
|
||||
override DataFlowCall getCall() { result = this.getCallInstruction() }
|
||||
|
||||
override ReturnKind getReturnKind() { result = TNormalReturnKind(this.getIndirectionIndex()) }
|
||||
}
|
||||
|
||||
private class SideEffectOutNode extends OutNode, IndirectArgumentOutNode {
|
||||
override DataFlowCall getCall() { result.asCallInstruction() = this.getCallInstruction() }
|
||||
override DataFlowCall getCall() { result = this.getCallInstruction() }
|
||||
|
||||
override ReturnKind getReturnKind() {
|
||||
result = TIndirectReturnKind(this.getArgumentIndex(), this.getIndirectionIndex())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An output node that is part of a summary. An output node is needed when the
|
||||
* model contains a synthesized call (`SummaryCall`) and the return value of
|
||||
* that call is needed by the summary (for example when the model has flow from
|
||||
* `Argument[0].ReturnValue`).
|
||||
*/
|
||||
private class SummaryOutNode extends OutNode, FlowSummaryNode {
|
||||
private SummaryCall call;
|
||||
private ReturnKind kind_;
|
||||
|
||||
SummaryOutNode() {
|
||||
FlowSummaryImpl::Private::summaryOutNode(call.getReceiver(), this.getSummaryNode(), kind_)
|
||||
}
|
||||
|
||||
override DataFlowCall getCall() { result = call }
|
||||
|
||||
override ReturnKind getReturnKind() { result = kind_ }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that can read the value returned from `call` with return kind
|
||||
* `kind`.
|
||||
@@ -810,10 +721,6 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
v = n1.asIndirectVariable(globalDef.getIndirection())
|
||||
)
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(),
|
||||
n2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -822,35 +729,25 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
* value of `node1`.
|
||||
*
|
||||
* The boolean `certain` is true if the destination address does not involve
|
||||
* any pointer arithmetic, and false otherwise. This has to do with whether a
|
||||
* store step can be used to clear a field (see `clearsContent`).
|
||||
* any pointer arithmetic, and false otherwise.
|
||||
*/
|
||||
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
|
||||
exists(
|
||||
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
|
||||
StoreInstruction store
|
||||
|
|
||||
postFieldUpdate = node2 and
|
||||
predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolean certain) {
|
||||
exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store |
|
||||
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
|
||||
postFieldUpdate.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain)
|
||||
node2.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(),
|
||||
numberOfLoads, certain)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = postFieldUpdate.getUpdatedField() and
|
||||
fc.getField() = node2.getUpdatedField() and
|
||||
fc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
uc.getAField() = node2.getUpdatedField() and
|
||||
uc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode()) and
|
||||
certain = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -937,10 +834,6 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
uc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1014,213 +907,21 @@ class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDataFlowCallable =
|
||||
TSourceCallable(Cpp::Declaration decl) {
|
||||
not decl instanceof FlowSummaryImpl::Public::SummarizedCallable
|
||||
} or
|
||||
TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c)
|
||||
|
||||
/**
|
||||
* A callable, which may be:
|
||||
* - a function (that may contain code)
|
||||
* - a summarized function (that may contain only `FlowSummaryNode`s)
|
||||
* - a variable (this is used as context for global initialization, and also
|
||||
* for the mid-point in interprocedural data flow between a write and read
|
||||
* of a global variable in different functions).
|
||||
* When flow crosses from one _enclosing callable_ to another, the
|
||||
* interprocedural data-flow library discards call contexts and inserts a node
|
||||
* in the big-step relation used for human-readable path explanations.
|
||||
* A function that may contain code or a variable that may contain itself. When
|
||||
* flow crosses from one _enclosing callable_ to another, the interprocedural
|
||||
* data-flow library discards call contexts and inserts a node in the big-step
|
||||
* relation used for human-readable path explanations.
|
||||
*/
|
||||
class DataFlowCallable extends TDataFlowCallable {
|
||||
/** Gets the location of this callable. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets a textual representation of this callable. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets the `Declaration` corresponding to this callable if it exists in the database.
|
||||
* For summarized callables (which may not exist in the database), use `asSummarizedCallable`.
|
||||
*/
|
||||
Cpp::Declaration asSourceCallable() { this = TSourceCallable(result) }
|
||||
|
||||
/**
|
||||
* Gets the underlying summarized callable, if
|
||||
* this callable is generated from a models-as-data
|
||||
* model.
|
||||
*/
|
||||
FlowSummaryImpl::Public::SummarizedCallable asSummarizedCallable() {
|
||||
this = TSummarizedCallable(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying `Declaration` of this `DataFlowCallable`. This
|
||||
* predicate returns a result for both source and summarized callables.
|
||||
*/
|
||||
Cpp::Declaration getUnderlyingCallable() {
|
||||
result = this.asSummarizedCallable() or // SummarizedCallable = Function (in CPP)
|
||||
result = this.asSourceCallable()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A source callable, conceptually, a function in the source code for the
|
||||
* purpose of computing data flow. In practice this excludes functions that
|
||||
* are summarized using models-as-data (as we don't want to create
|
||||
* unmodeled flows or duplicate paths), and includes variables (for reasons
|
||||
* explained in `DataFlowCallable`).
|
||||
*/
|
||||
class SourceCallable extends DataFlowCallable, TSourceCallable {
|
||||
Cpp::Declaration decl;
|
||||
|
||||
SourceCallable() { this = TSourceCallable(decl) }
|
||||
|
||||
override string toString() { result = decl.toString() }
|
||||
|
||||
override Location getLocation() { result = decl.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A summarized callable, that is, a function synthesized from one or more
|
||||
* models-as-data models as a place to contain the corresponding
|
||||
* `FlowSummaryNode`s.
|
||||
*/
|
||||
class SummarizedCallable extends DataFlowCallable, TSummarizedCallable {
|
||||
FlowSummaryImpl::Public::SummarizedCallable sc;
|
||||
|
||||
SummarizedCallable() { this = TSummarizedCallable(sc) }
|
||||
|
||||
override string toString() { result = sc.toString() }
|
||||
|
||||
override Location getLocation() { result = sc.getLocation() }
|
||||
}
|
||||
class DataFlowCallable = Cpp::Declaration;
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
class DataFlowType = Type;
|
||||
|
||||
cached
|
||||
private newtype TDataFlowCall =
|
||||
TNormalCall(CallInstruction call) or
|
||||
TSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
) {
|
||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||
}
|
||||
|
||||
/**
|
||||
* A function call relevant for data flow. This includes calls from source
|
||||
* code and calls inside library callables with a flow summary.
|
||||
*/
|
||||
class DataFlowCall extends TDataFlowCall {
|
||||
/**
|
||||
* Gets the underlying data flow call instruction, if any.
|
||||
*/
|
||||
CallInstruction asCallInstruction() { none() }
|
||||
|
||||
/**
|
||||
* Gets the operand the specifies the target function of the call.
|
||||
*/
|
||||
CallTargetOperand getCallTargetOperand() { none() }
|
||||
|
||||
/**
|
||||
* Gets the `Function` that the call targets, if this is statically known.
|
||||
*/
|
||||
DataFlowCallable getStaticCallTarget() { none() }
|
||||
|
||||
/**
|
||||
* Gets the `index`'th argument operand. The qualifier is considered to have index `-1`.
|
||||
*/
|
||||
ArgumentOperand getArgumentOperand(int index) { none() }
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getArgument(int index) { result = this.getArgumentOperand(index).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final int getNumberOfArguments() { result = count(this.getArgumentOperand(_)) }
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable, if any.
|
||||
*/
|
||||
DataFlowCallable getEnclosingCallable() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this call.
|
||||
*/
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets the location of this call.
|
||||
*/
|
||||
Location getLocation() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function call relevant for data flow, that exists in source code.
|
||||
*/
|
||||
private class NormalCall extends DataFlowCall, TNormalCall {
|
||||
private CallInstruction call;
|
||||
|
||||
NormalCall() { this = TNormalCall(call) }
|
||||
|
||||
override CallInstruction asCallInstruction() { result = call }
|
||||
|
||||
override CallTargetOperand getCallTargetOperand() { result = call.getCallTargetOperand() }
|
||||
|
||||
override DataFlowCallable getStaticCallTarget() {
|
||||
result.getUnderlyingCallable() = call.getStaticCallTarget()
|
||||
}
|
||||
|
||||
override ArgumentOperand getArgumentOperand(int index) { result = call.getArgumentOperand(index) }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.getUnderlyingCallable() = call.getEnclosingFunction()
|
||||
}
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A synthesized call inside a callable with a flow summary.
|
||||
*
|
||||
* For example, consider the function:
|
||||
* ```
|
||||
* int myFunction(int (*funPtr)());
|
||||
* ```
|
||||
* with an accompanying models-as-data flow summary involving `funPtr` (for
|
||||
* example from `Argument[0].ReturnValue` to `ReturnValue`). A `SummaryCall`
|
||||
* will be synthesized representing a call to `funPtr` inside `myFunction`,
|
||||
* so that flow can be connected as described in the model.
|
||||
*/
|
||||
class SummaryCall extends DataFlowCall, TSummaryCall {
|
||||
private FlowSummaryImpl::Public::SummarizedCallable c;
|
||||
private FlowSummaryImpl::Private::SummaryNode receiver;
|
||||
|
||||
SummaryCall() { this = TSummaryCall(c, receiver) }
|
||||
|
||||
/**
|
||||
* Gets the data flow node that holds the address of the function this call
|
||||
* targets.
|
||||
*/
|
||||
FlowSummaryImpl::Private::SummaryNode getReceiver() { result = receiver }
|
||||
|
||||
// no implementation for `getCallTargetOperand()`, `getStaticCallTarget()`
|
||||
// or `getArgumentOperand(int index)`. This is because the flow summary
|
||||
// library is responsible for finding the call target, and there are no
|
||||
// IR nodes available for the call target operand or argument operands.
|
||||
override DataFlowCallable getEnclosingCallable() { result = TSummarizedCallable(c) }
|
||||
|
||||
override string toString() { result = "[summary] call to " + receiver + " in " + c }
|
||||
|
||||
override UnknownLocation getLocation() { any() }
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends CallInstruction {
|
||||
DataFlowCallable getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
module IsUnreachableInCall {
|
||||
@@ -1249,7 +950,7 @@ module IsUnreachableInCall {
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) {
|
||||
exists(
|
||||
InstructionDirectParameterNode paramNode, ConstantIntegralTypeArgumentNode arg,
|
||||
DirectParameterNode paramNode, ConstantIntegralTypeArgumentNode arg,
|
||||
IntegerConstantInstruction constant, int k, Operand left, Operand right, IRBlock block
|
||||
|
|
||||
// arg flows into `paramNode`
|
||||
@@ -1308,34 +1009,17 @@ predicate nodeIsHidden(Node n) {
|
||||
n instanceof InitialGlobalValue
|
||||
}
|
||||
|
||||
predicate neverSkipInPathGraph(Node n) {
|
||||
// Always show the right-hand side of assignments in the path graph
|
||||
exists(n.asDefinition())
|
||||
or
|
||||
exists(n.asIndirectDefinition())
|
||||
}
|
||||
|
||||
class LambdaCallKind = Unit;
|
||||
|
||||
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
|
||||
creation.asInstruction().(FunctionAddressInstruction).getFunctionSymbol() = c.asSourceCallable() and
|
||||
exists(kind)
|
||||
}
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
|
||||
|
||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
|
||||
call.(SummaryCall).getReceiver() = receiver.(FlowSummaryNode).getSummaryNode() and
|
||||
exists(kind)
|
||||
}
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
||||
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
predicate knownSourceModel(Node source, string model) { none() }
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { none() }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
@@ -1343,15 +1027,7 @@ predicate knownSinkModel(Node sink, string model) { none() }
|
||||
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
|
||||
* by default as a heuristic.
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) {
|
||||
p instanceof IndirectParameterNode
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
exists(DataFlowCallable c, ParameterPosition pos |
|
||||
p.isParameterOf(c, pos) and
|
||||
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asSummarizedCallable(), pos)
|
||||
)
|
||||
}
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { p instanceof IndirectParameterNode }
|
||||
|
||||
private predicate fieldHasApproxName(Field f, string s) {
|
||||
s = f.getName().charAt(0) and
|
||||
@@ -1420,7 +1096,7 @@ private predicate localFlowStepWithSummaries(Node node1, Node node2) {
|
||||
or
|
||||
readStep(node1, _, node2)
|
||||
or
|
||||
DataFlowImplCommon::argumentValueFlowsThrough(node1, _, node2, _)
|
||||
DataFlowImplCommon::argumentValueFlowsThrough(node1, _, node2)
|
||||
}
|
||||
|
||||
/** Holds if `node` flows to a node that is used in a `SwitchInstruction`. */
|
||||
@@ -1463,7 +1139,7 @@ private predicate getAdditionalFlowIntoCallNodeTermStep(Node node1, Node node2)
|
||||
/** Gets the `IRVariable` associated with the parameter node `p`. */
|
||||
pragma[nomagic]
|
||||
private IRVariable getIRVariableForParameterNode(ParameterNode p) {
|
||||
result = p.(InstructionDirectParameterNode).getIRVariable()
|
||||
result = p.(DirectParameterNode).getIRVariable()
|
||||
or
|
||||
result.getAst() = p.(IndirectParameterNode).getParameter()
|
||||
}
|
||||
@@ -1583,85 +1259,3 @@ predicate validParameterAliasStep(Node node1, Node node2) {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isTopLevel(Cpp::Stmt s) { any(Function f).getBlock().getAStmt() = s }
|
||||
|
||||
private Cpp::Stmt getAChainedBranch(Cpp::IfStmt s) {
|
||||
result = s.getThen()
|
||||
or
|
||||
exists(Cpp::Stmt elseBranch | s.getElse() = elseBranch |
|
||||
result = getAChainedBranch(elseBranch)
|
||||
or
|
||||
result = elseBranch and not elseBranch instanceof Cpp::IfStmt
|
||||
)
|
||||
}
|
||||
|
||||
private Instruction getAnInstruction(Node n) {
|
||||
result = n.asInstruction()
|
||||
or
|
||||
not n instanceof InstructionNode and
|
||||
result = n.asOperand().getUse()
|
||||
or
|
||||
result = n.(SsaPhiNode).getPhiNode().getBasicBlock().getFirstInstruction()
|
||||
or
|
||||
n.(IndirectInstruction).hasInstructionAndIndirectionIndex(result, _)
|
||||
or
|
||||
not n instanceof IndirectInstruction and
|
||||
exists(Operand operand |
|
||||
n.(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and
|
||||
result = operand.getUse()
|
||||
)
|
||||
or
|
||||
result = getAnInstruction(n.(PostUpdateNode).getPreUpdateNode())
|
||||
}
|
||||
|
||||
private newtype TDataFlowSecondLevelScope =
|
||||
TTopLevelIfBranch(Cpp::Stmt s) {
|
||||
exists(Cpp::IfStmt ifstmt | s = getAChainedBranch(ifstmt) and isTopLevel(ifstmt))
|
||||
} or
|
||||
TTopLevelSwitchCase(Cpp::SwitchCase s) {
|
||||
exists(Cpp::SwitchStmt switchstmt | s = switchstmt.getASwitchCase() and isTopLevel(switchstmt))
|
||||
}
|
||||
|
||||
/**
|
||||
* A second-level control-flow scope in a `switch` or a chained `if` statement.
|
||||
*
|
||||
* This is a `switch` case or a branch of a chained `if` statement, given that
|
||||
* the `switch` or `if` statement is top level, that is, it is not nested inside
|
||||
* other CFG constructs.
|
||||
*/
|
||||
class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Cpp::Stmt s | this = TTopLevelIfBranch(s) | result = s.toString())
|
||||
or
|
||||
exists(Cpp::SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.toString())
|
||||
}
|
||||
|
||||
/** Gets the primary location of this element. */
|
||||
Cpp::Location getLocation() {
|
||||
exists(Cpp::Stmt s | this = TTopLevelIfBranch(s) | result = s.getLocation())
|
||||
or
|
||||
exists(Cpp::SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.getLocation())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a statement directly contained in this scope. For an `if` branch, this
|
||||
* is the branch itself, and for a `switch case`, this is one the statements
|
||||
* of that case branch.
|
||||
*/
|
||||
private Cpp::Stmt getAStmt() {
|
||||
exists(Cpp::Stmt s | this = TTopLevelIfBranch(s) | result = s)
|
||||
or
|
||||
exists(Cpp::SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.getAStmt())
|
||||
}
|
||||
|
||||
/** Gets a data-flow node nested within this scope. */
|
||||
Node getANode() {
|
||||
getAnInstruction(result).getAst().(Cpp::ControlFlowNode).getEnclosingStmt().getParentStmt*() =
|
||||
this.getAStmt()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the second-level scope containing the node `n`, if any. */
|
||||
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
|
||||
|
||||
@@ -10,7 +10,6 @@ private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import DataFlowPrivate
|
||||
private import ModelUtil
|
||||
private import SsaInternals as Ssa
|
||||
@@ -60,17 +59,7 @@ private newtype TIRDataFlowNode =
|
||||
)
|
||||
} or
|
||||
TFinalGlobalValue(Ssa::GlobalUse globalUse) or
|
||||
TInitialGlobalValue(Ssa::GlobalDef globalUse) or
|
||||
TBodyLessParameterNodeImpl(Parameter p, int indirectionIndex) {
|
||||
// Rule out parameters of catch blocks.
|
||||
not exists(p.getCatchBlock()) and
|
||||
// We subtract one because `getMaxIndirectionsForType` returns the maximum
|
||||
// indirection for a glvalue of a given type, and this doesn't apply to
|
||||
// parameters.
|
||||
indirectionIndex = [0 .. Ssa::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1] and
|
||||
not any(InitializeParameterInstruction init).getParameter() = p
|
||||
} or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn)
|
||||
TInitialGlobalValue(Ssa::GlobalDef globalUse)
|
||||
|
||||
/**
|
||||
* An operand that is defined by a `FieldAddressInstruction`.
|
||||
@@ -398,7 +387,7 @@ class Node extends TIRDataFlowNode {
|
||||
index = 0 and
|
||||
result = this.(ExplicitParameterNode).getParameter()
|
||||
or
|
||||
this.(IndirectParameterNode).getIndirectionIndex() = index and
|
||||
this.(IndirectParameterNode).hasInstructionAndIndirectionIndex(_, index) and
|
||||
result = this.(IndirectParameterNode).getParameter()
|
||||
}
|
||||
|
||||
@@ -749,65 +738,37 @@ class InitialGlobalValue extends Node, TInitialGlobalValue {
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A node representing a parameter for a function with no body.
|
||||
* A node representing an indirection of a parameter.
|
||||
*/
|
||||
class BodyLessParameterNodeImpl extends Node, TBodyLessParameterNodeImpl {
|
||||
Parameter p;
|
||||
int indirectionIndex;
|
||||
class IndirectParameterNode extends Node instanceof IndirectInstruction {
|
||||
InitializeParameterInstruction init;
|
||||
|
||||
BodyLessParameterNodeImpl() { this = TBodyLessParameterNodeImpl(p, indirectionIndex) }
|
||||
IndirectParameterNode() { IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) }
|
||||
|
||||
int getArgumentIndex() { init.hasIndex(result) }
|
||||
|
||||
/** Gets the parameter whose indirection is initialized. */
|
||||
Parameter getParameter() { result = init.getParameter() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = p.getFunction() }
|
||||
override Declaration getFunction() { result = init.getEnclosingFunction() }
|
||||
|
||||
/** Gets the indirection index of this node. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override DataFlowType getType() {
|
||||
result = getTypeImpl(p.getUnderlyingType(), this.getIndirectionIndex())
|
||||
/** Gets the underlying operand and the underlying indirection index. */
|
||||
predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
|
||||
IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index)
|
||||
}
|
||||
|
||||
final override Location getLocationImpl() {
|
||||
result = unique( | | p.getLocation())
|
||||
or
|
||||
count(p.getLocation()) != 1 and
|
||||
result instanceof UnknownDefaultLocation
|
||||
override Location getLocationImpl() { result = this.getParameter().getLocation() }
|
||||
|
||||
override string toStringImpl() {
|
||||
exists(string prefix | prefix = stars(this) |
|
||||
result = prefix + this.getParameter().toString()
|
||||
or
|
||||
not exists(this.getParameter()) and
|
||||
result = prefix + "this"
|
||||
)
|
||||
}
|
||||
|
||||
final override string toStringImpl() {
|
||||
exists(string prefix | prefix = stars(this) | result = prefix + p.toString())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node used to model flow summaries. That is, a dataflow node
|
||||
* that is synthesized to represent a parameter, return value, or other part
|
||||
* of a models-as-data modeled function.
|
||||
*/
|
||||
class FlowSummaryNode extends Node, TFlowSummaryNode {
|
||||
/**
|
||||
* Gets the models-as-data `SummaryNode` associated with this dataflow
|
||||
* `FlowSummaryNode`.
|
||||
*/
|
||||
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
|
||||
|
||||
/**
|
||||
* Gets the summarized callable that this node belongs to.
|
||||
*/
|
||||
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
|
||||
result = this.getSummaryNode().getSummarizedCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable. For a `FlowSummaryNode` this is always the
|
||||
* summarized function this node is part of.
|
||||
*/
|
||||
override Declaration getEnclosingCallable() { result = this.getSummarizedCallable() }
|
||||
|
||||
override Location getLocationImpl() { result = this.getSummarizedCallable().getLocation() }
|
||||
|
||||
override string toStringImpl() { result = this.getSummaryNode().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -865,9 +826,6 @@ class IndirectArgumentOutNode extends PostUpdateNodeImpl {
|
||||
|
||||
CallInstruction getCallInstruction() { result.getAnArgumentOperand() = operand }
|
||||
|
||||
/**
|
||||
* Gets the `Function` that the call targets, if this is statically known.
|
||||
*/
|
||||
Function getStaticCallTarget() { result = this.getCallInstruction().getStaticCallTarget() }
|
||||
|
||||
override string toStringImpl() {
|
||||
@@ -1662,36 +1620,68 @@ class IndirectExprNode extends Node instanceof IndirectExprNodeBase {
|
||||
}
|
||||
}
|
||||
|
||||
abstract private class AbstractParameterNode extends Node {
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
|
||||
* and implicit parameters such as `this` in `x.f()`.
|
||||
*
|
||||
* To match a specific kind of parameter, consider using one of the subclasses
|
||||
* `ExplicitParameterNode`, `ThisParameterNode`, or
|
||||
* `ParameterIndirectionNode`.
|
||||
*/
|
||||
class ParameterNode extends Node {
|
||||
ParameterNode() {
|
||||
// To avoid making this class abstract, we enumerate its values here
|
||||
this.asInstruction() instanceof InitializeParameterInstruction
|
||||
or
|
||||
this instanceof IndirectParameterNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of `f` at the specified position. The
|
||||
* implicit `this` parameter is considered to have position `-1`, and
|
||||
* pointer-indirection parameters are at further negative positions.
|
||||
*/
|
||||
abstract predicate isParameterOf(DataFlowCallable f, ParameterPosition pos);
|
||||
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
|
||||
|
||||
/** Gets the `Parameter` associated with this node, if it exists. */
|
||||
Parameter getParameter() { none() } // overridden by subclasses
|
||||
}
|
||||
|
||||
abstract private class AbstractIndirectParameterNode extends AbstractParameterNode {
|
||||
/** Gets the indirection index of this parameter node. */
|
||||
abstract int getIndirectionIndex();
|
||||
/** An explicit positional parameter, including `this`, but not `...`. */
|
||||
class DirectParameterNode extends InstructionNode {
|
||||
override InitializeParameterInstruction instr;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the `IRVariable` that this parameter references.
|
||||
*/
|
||||
IRVariable getIRVariable() { result = instr.getIRVariable() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A node representing an indirection of a parameter.
|
||||
*/
|
||||
final class IndirectParameterNode = AbstractIndirectParameterNode;
|
||||
/** An explicit positional parameter, not including `this` or `...`. */
|
||||
private class ExplicitParameterNode extends ParameterNode, DirectParameterNode {
|
||||
ExplicitParameterNode() { exists(instr.getParameter()) }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
|
||||
IndirectInstructionParameterNode node, int argumentIndex, int indirectionIndex
|
||||
) {
|
||||
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and
|
||||
node.getArgumentIndex() = argumentIndex
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = instr.getParameter().toString() }
|
||||
|
||||
override Parameter getParameter() { result = instr.getParameter() }
|
||||
}
|
||||
|
||||
/** An implicit `this` parameter. */
|
||||
class ThisParameterNode extends ParameterNode, DirectParameterNode {
|
||||
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
pos.(DirectPosition).getIndex() = -1 and instr.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = "this" }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -1702,167 +1692,23 @@ private predicate indirectPositionHasArgumentIndexAndIndex(
|
||||
pos.getIndirectionIndex() = indirectionIndex
|
||||
}
|
||||
|
||||
private class IndirectInstructionParameterNode extends AbstractIndirectParameterNode instanceof IndirectInstruction
|
||||
{
|
||||
InitializeParameterInstruction init;
|
||||
pragma[noinline]
|
||||
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
|
||||
IndirectParameterNode node, int argumentIndex, int indirectionIndex
|
||||
) {
|
||||
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and
|
||||
node.getArgumentIndex() = argumentIndex
|
||||
}
|
||||
|
||||
IndirectInstructionParameterNode() {
|
||||
IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _)
|
||||
}
|
||||
|
||||
int getArgumentIndex() { init.hasIndex(result) }
|
||||
|
||||
override string toStringImpl() {
|
||||
exists(string prefix | prefix = stars(this) |
|
||||
result = prefix + this.getParameter().toString()
|
||||
or
|
||||
not exists(this.getParameter()) and
|
||||
result = prefix + "this"
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the parameter whose indirection is initialized. */
|
||||
override Parameter getParameter() { result = init.getParameter() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = init.getEnclosingFunction() }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
this.getEnclosingCallable() = f.getUnderlyingCallable() and
|
||||
/** A synthetic parameter to model the pointed-to object of a pointer parameter. */
|
||||
class ParameterIndirectionNode extends ParameterNode instanceof IndirectParameterNode {
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
IndirectParameterNode.super.getEnclosingCallable() = f and
|
||||
exists(int argumentIndex, int indirectionIndex |
|
||||
indirectPositionHasArgumentIndexAndIndex(pos, argumentIndex, indirectionIndex) and
|
||||
indirectParameterNodeHasArgumentIndexAndIndex(this, argumentIndex, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the underlying operand and the underlying indirection index. */
|
||||
predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
|
||||
IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index)
|
||||
}
|
||||
|
||||
final override int getIndirectionIndex() { this.hasInstructionAndIndirectionIndex(init, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
|
||||
* and implicit parameters such as `this` in `x.f()`.
|
||||
*
|
||||
* To match a specific kind of parameter, consider using one of the subclasses
|
||||
* `ExplicitParameterNode`, `ThisParameterNode`, or
|
||||
* `ParameterIndirectionNode`.
|
||||
*/
|
||||
final class ParameterNode = AbstractParameterNode;
|
||||
|
||||
abstract private class AbstractDirectParameterNode extends AbstractParameterNode { }
|
||||
|
||||
/** An explicit positional parameter, including `this`, but not `...`. */
|
||||
final class DirectParameterNode = AbstractDirectParameterNode;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A non-indirect parameter node that is represented as an `Instruction`.
|
||||
*/
|
||||
abstract class InstructionDirectParameterNode extends InstructionNode, AbstractDirectParameterNode {
|
||||
final override InitializeParameterInstruction instr;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the `IRVariable` that this parameter references.
|
||||
*/
|
||||
final IRVariable getIRVariable() { result = instr.getIRVariable() }
|
||||
}
|
||||
|
||||
abstract private class AbstractExplicitParameterNode extends AbstractDirectParameterNode { }
|
||||
|
||||
final class ExplicitParameterNode = AbstractExplicitParameterNode;
|
||||
|
||||
/** An explicit positional parameter, not including `this` or `...`. */
|
||||
private class ExplicitParameterInstructionNode extends AbstractExplicitParameterNode,
|
||||
InstructionDirectParameterNode
|
||||
{
|
||||
ExplicitParameterInstructionNode() { exists(instr.getParameter()) }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
f.getUnderlyingCallable().(Function).getParameter(pos.(DirectPosition).getIndex()) =
|
||||
instr.getParameter()
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = instr.getParameter().toString() }
|
||||
|
||||
override Parameter getParameter() { result = instr.getParameter() }
|
||||
}
|
||||
|
||||
/** An implicit `this` parameter. */
|
||||
class ThisParameterInstructionNode extends AbstractExplicitParameterNode,
|
||||
InstructionDirectParameterNode
|
||||
{
|
||||
ThisParameterInstructionNode() { instr.getIRVariable() instanceof IRThisVariable }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
pos.(DirectPosition).getIndex() = -1 and
|
||||
instr.getEnclosingFunction() = f.getUnderlyingCallable()
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = "this" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter node that is part of a summary.
|
||||
*/
|
||||
class SummaryParameterNode extends AbstractParameterNode, FlowSummaryNode {
|
||||
SummaryParameterNode() {
|
||||
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), _)
|
||||
}
|
||||
|
||||
private ParameterPosition getPosition() {
|
||||
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), result)
|
||||
}
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition p) {
|
||||
c.getUnderlyingCallable() = this.getSummarizedCallable() and
|
||||
p = this.getPosition()
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectBodyLessParameterNode extends AbstractExplicitParameterNode,
|
||||
BodyLessParameterNodeImpl
|
||||
{
|
||||
DirectBodyLessParameterNode() { indirectionIndex = 0 }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
exists(Function func |
|
||||
this.getFunction() = func and
|
||||
f.asSourceCallable() = func and
|
||||
func.getParameter(pos.(DirectPosition).getIndex()) = p
|
||||
)
|
||||
}
|
||||
|
||||
override Parameter getParameter() { result = p }
|
||||
}
|
||||
|
||||
private class IndirectBodyLessParameterNode extends AbstractIndirectParameterNode,
|
||||
BodyLessParameterNodeImpl
|
||||
{
|
||||
IndirectBodyLessParameterNode() { not this instanceof DirectBodyLessParameterNode }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
exists(Function func, int argumentPosition |
|
||||
this.getFunction() = func and
|
||||
f.asSourceCallable() = func and
|
||||
indirectPositionHasArgumentIndexAndIndex(pos, argumentPosition, indirectionIndex) and
|
||||
func.getParameter(argumentPosition) = p
|
||||
)
|
||||
}
|
||||
|
||||
override int getIndirectionIndex() {
|
||||
result = BodyLessParameterNodeImpl.super.getIndirectionIndex()
|
||||
}
|
||||
|
||||
override Parameter getParameter() { result = p }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1907,22 +1753,6 @@ abstract private class PartialDefinitionNode extends PostUpdateNode {
|
||||
abstract Expr getDefinedExpr();
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PostUpdateNode` that is part of a flow summary. These are synthesized,
|
||||
* for example, when a models-as-data summary models a write to a field since
|
||||
* the write needs to target a `PostUpdateNode`.
|
||||
*/
|
||||
class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
|
||||
SummaryPostUpdateNode() {
|
||||
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(), _)
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() {
|
||||
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(),
|
||||
result.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that represents the value of a variable after a function call that
|
||||
* may have changed the variable because it's passed by reference.
|
||||
@@ -2059,20 +1889,10 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step. This relation is only used for local dataflow
|
||||
* (for example `DataFlow::localFlow(source, sink)`) so it contains
|
||||
* special cases that should only apply to local dataflow.
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// common dataflow steps
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// models-as-data summarized flow for local data flow (i.e. special case for flow
|
||||
// through calls to modeled functions, without relying on global dataflow to join
|
||||
// the dots).
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
|
||||
}
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
|
||||
|
||||
private predicate indirectionOperandFlow(RawIndirectOperand nodeFrom, Node nodeTo) {
|
||||
nodeFrom != nodeTo and
|
||||
@@ -2138,54 +1958,45 @@ private module Cached {
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* This is the local flow predicate that's used as a building block in both
|
||||
* local and global data flow. It may have less flow than the `localFlowStep`
|
||||
* predicate.
|
||||
* This is the local flow predicate that's used as a building block in global
|
||||
* data flow. It may have less flow than the `localFlowStep` predicate.
|
||||
*/
|
||||
cached
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
(
|
||||
// Post update node -> Node flow
|
||||
Ssa::postUpdateFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// Def-use/Use-use flow
|
||||
Ssa::ssaFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// Operand -> Instruction flow
|
||||
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||
or
|
||||
// Instruction -> Operand flow
|
||||
exists(Instruction iFrom, Operand opTo |
|
||||
iFrom = nodeFrom.asInstruction() and opTo = nodeTo.asOperand()
|
||||
|
|
||||
simpleOperandLocalFlowStep(iFrom, opTo) and
|
||||
// Omit when the instruction node also represents the operand.
|
||||
not iFrom = Ssa::getIRRepresentationOfOperand(opTo)
|
||||
)
|
||||
or
|
||||
// Phi node -> Node flow
|
||||
Ssa::fromPhiNode(nodeFrom, nodeTo)
|
||||
or
|
||||
// Indirect operand -> (indirect) instruction flow
|
||||
indirectionOperandFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// Indirect instruction -> indirect operand flow
|
||||
indirectionInstructionFlow(nodeFrom, nodeTo)
|
||||
) and
|
||||
model = ""
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Post update node -> Node flow
|
||||
Ssa::postUpdateFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// Def-use/Use-use flow
|
||||
Ssa::ssaFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// Operand -> Instruction flow
|
||||
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||
or
|
||||
// Instruction -> Operand flow
|
||||
exists(Instruction iFrom, Operand opTo |
|
||||
iFrom = nodeFrom.asInstruction() and opTo = nodeTo.asOperand()
|
||||
|
|
||||
simpleOperandLocalFlowStep(iFrom, opTo) and
|
||||
// Omit when the instruction node also represents the operand.
|
||||
not iFrom = Ssa::getIRRepresentationOfOperand(opTo)
|
||||
)
|
||||
or
|
||||
// Phi node -> Node flow
|
||||
Ssa::fromPhiNode(nodeFrom, nodeTo)
|
||||
or
|
||||
// Indirect operand -> (indirect) instruction flow
|
||||
indirectionOperandFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// Indirect instruction -> indirect operand flow
|
||||
indirectionInstructionFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow through modeled functions
|
||||
modelFlow(nodeFrom, nodeTo, model)
|
||||
modelFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// Reverse flow: data that flows from the definition node back into the indirection returned
|
||||
// by a function. This allows data to flow 'in' through references returned by a modeled
|
||||
// function such as `operator[]`.
|
||||
reverseFlow(nodeFrom, nodeTo) and
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
reverseFlow(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
|
||||
@@ -2200,13 +2011,12 @@ private module Cached {
|
||||
opTo.getDef() = iFrom
|
||||
}
|
||||
|
||||
private predicate modelFlow(Node nodeFrom, Node nodeTo, string model) {
|
||||
private predicate modelFlow(Node nodeFrom, Node nodeTo) {
|
||||
exists(
|
||||
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
|
||||
|
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasDataFlow(modelIn, modelOut) and
|
||||
model = "DataFlowFunction"
|
||||
func.hasDataFlow(modelIn, modelOut)
|
||||
|
|
||||
nodeFrom = callInput(call, modelIn) and
|
||||
nodeTo = callOutput(call, modelOut)
|
||||
@@ -2427,8 +2237,6 @@ private Field getAFieldWithSize(Union u, int bytes) {
|
||||
cached
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f, int indirectionIndex) {
|
||||
// the indirection index for field content starts at 1 (because `TFieldContent` is thought of as
|
||||
// the address of the field, `FieldAddress` in the IR).
|
||||
indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(f.getUnspecifiedType())] and
|
||||
// Reads and writes of union fields are tracked using `UnionContent`.
|
||||
not f.getDeclaringType() instanceof Union
|
||||
@@ -2438,8 +2246,7 @@ private newtype TContent =
|
||||
f = u.getAField() and
|
||||
bytes = getFieldSize(f) and
|
||||
// We key `UnionContent` by the union instead of its fields since a write to one
|
||||
// field can be read by any read of the union's fields. Again, the indirection index
|
||||
// is 1-based (because 0 is considered the address).
|
||||
// field can be read by any read of the union's fields.
|
||||
indirectionIndex =
|
||||
[1 .. max(Ssa::getMaxIndirectionsForType(getAFieldWithSize(u, bytes).getUnspecifiedType()))]
|
||||
)
|
||||
@@ -2473,14 +2280,16 @@ class Content extends TContent {
|
||||
abstract predicate impliesClearOf(Content c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string consisting of `n` star characters ("*"), where n >= 0. This is
|
||||
* used to represent indirection.
|
||||
*/
|
||||
bindingset[n]
|
||||
string repeatStars(int n) { result = concat(int i | i in [1 .. n] | "*") }
|
||||
|
||||
private module ContentStars {
|
||||
private int maxNumberOfIndirections() { result = max(any(Content c).getIndirectionIndex()) }
|
||||
|
||||
private string repeatStars(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n = [1 .. maxNumberOfIndirections()] and
|
||||
result = "*" + repeatStars(n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
|
||||
* output for `c`.
|
||||
@@ -2560,12 +2369,6 @@ class UnionContent extends Content, TUnionContent {
|
||||
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
|
||||
*/
|
||||
class ContentSet instanceof Content {
|
||||
/**
|
||||
* Holds if this content set is the singleton `{c}`. At present, this is
|
||||
* the only kind of content set supported in C/C++.
|
||||
*/
|
||||
predicate isSingleton(Content c) { this = c }
|
||||
|
||||
/** Gets a content that may be stored into when storing into this set. */
|
||||
Content getAStoreContent() { result = this }
|
||||
|
||||
@@ -2783,5 +2586,5 @@ class AdditionalCallTarget extends Unit {
|
||||
/**
|
||||
* Gets a viable target for `call`.
|
||||
*/
|
||||
abstract Declaration viableTarget(Call call);
|
||||
abstract DataFlowCallable viableTarget(Call call);
|
||||
}
|
||||
|
||||
@@ -3,26 +3,12 @@
|
||||
* `toString` for `Instruction` and `Operand` dataflow nodes.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Unit
|
||||
private import Node0ToString
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Gets the string representation of the unconverted expression `loc` if
|
||||
* `loc` is an `Expression`.
|
||||
*
|
||||
* Otherwise, this gets the string representation of `loc`.
|
||||
*/
|
||||
private string unconvertedAstToString(Locatable loc) {
|
||||
result = loc.(Expr).getUnconverted().toString()
|
||||
or
|
||||
not loc instanceof Expr and
|
||||
result = loc.toString()
|
||||
}
|
||||
|
||||
private class NormalNode0ToString extends Node0ToString {
|
||||
NormalNode0ToString() {
|
||||
// Silence warning about `this` not being bound.
|
||||
@@ -32,10 +18,14 @@ private class NormalNode0ToString extends Node0ToString {
|
||||
override string instructionToString(Instruction i) {
|
||||
if i.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = unconvertedAstToString(i.getAst())
|
||||
else result = i.getAst().toString()
|
||||
}
|
||||
|
||||
override string operandToString(Operand op) { result = this.instructionToString(op.getDef()) }
|
||||
override string operandToString(Operand op) {
|
||||
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = op.getDef().getAst().toString()
|
||||
}
|
||||
|
||||
override string toExprString(Node n) {
|
||||
result = n.asExpr(0).toString()
|
||||
|
||||
@@ -10,7 +10,7 @@ private import PrintIRUtilities
|
||||
*/
|
||||
private string getFromFlow(Node node2, int order1, int order2) {
|
||||
exists(Node node1 |
|
||||
simpleLocalFlowStep(node1, node2, _) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
result = nodeId(node1, order1, order2)
|
||||
)
|
||||
}
|
||||
@@ -20,7 +20,7 @@ private string getFromFlow(Node node2, int order1, int order2) {
|
||||
*/
|
||||
private string getToFlow(Node node1, int order1, int order2) {
|
||||
exists(Node node2 |
|
||||
simpleLocalFlowStep(node1, node2, _) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
result = nodeId(node2, order1, order2)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,15 @@ private module SourceVariables {
|
||||
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||
}
|
||||
|
||||
private int maxNumberOfIndirections() { result = max(SourceVariable sv | | sv.getIndirection()) }
|
||||
|
||||
private string repeatStars(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n = [1 .. maxNumberOfIndirections()] and
|
||||
result = "*" + repeatStars(n - 1)
|
||||
}
|
||||
|
||||
class SourceVariable extends TSourceVariable {
|
||||
BaseSourceVariable base;
|
||||
int ind;
|
||||
@@ -64,27 +73,18 @@ private module SourceVariables {
|
||||
|
||||
import SourceVariables
|
||||
|
||||
/**
|
||||
* Holds if `indirectionIndex` is a valid non-zero indirection index for
|
||||
* operand `op`. That is, `indirectionIndex` is between 1 and the maximum
|
||||
* indirection for the operand's type.
|
||||
*/
|
||||
predicate hasIndirectOperand(Operand op, int indirectionIndex) {
|
||||
exists(CppType type, int m |
|
||||
not ignoreOperand(op) and
|
||||
type = getLanguageType(op) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
indirectionIndex = [1 .. m]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `(operand, indirectionIndex)` columns should be
|
||||
* assigned a `RawIndirectOperand` value.
|
||||
*/
|
||||
predicate hasRawIndirectOperand(Operand op, int indirectionIndex) {
|
||||
hasIndirectOperand(op, indirectionIndex) and
|
||||
not hasIRRepresentationOfIndirectOperand(op, indirectionIndex, _, _)
|
||||
exists(CppType type, int m |
|
||||
not ignoreOperand(op) and
|
||||
type = getLanguageType(op) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
indirectionIndex = [1 .. m] and
|
||||
not hasIRRepresentationOfIndirectOperand(op, indirectionIndex, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,8 +443,6 @@ class FinalParameterUse extends UseImpl, TFinalParameterUse {
|
||||
|
||||
Parameter getParameter() { result = p }
|
||||
|
||||
int getArgumentIndex() { result = p.getIndex() }
|
||||
|
||||
override Node getNode() { finalParameterNodeHasParameterAndIndex(result, p, ind) }
|
||||
|
||||
override int getIndirection() { result = ind + 1 }
|
||||
@@ -893,7 +891,7 @@ private predicate isArgumentOfCallableInstruction(DataFlowCall call, Instruction
|
||||
}
|
||||
|
||||
private predicate isArgumentOfCallableOperand(DataFlowCall call, Operand operand) {
|
||||
operand = call.getArgumentOperand(_)
|
||||
operand.(ArgumentOperand).getCall() = call
|
||||
or
|
||||
exists(FieldAddressInstruction fai |
|
||||
fai.getObjectAddressOperand() = operand and
|
||||
|
||||
@@ -6,7 +6,6 @@ private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
private import DataFlowPrivate
|
||||
private import TypeFlow
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
@@ -956,7 +955,11 @@ private module Cached {
|
||||
* Holds if the address computed by `operand` is guaranteed to write
|
||||
* to a specific address.
|
||||
*/
|
||||
private predicate isCertainAddress(Operand operand) { isPointerToSingleObject(operand.getDef()) }
|
||||
private predicate isCertainAddress(Operand operand) {
|
||||
valueNumberOfOperand(operand).getAnInstruction() instanceof VariableAddressInstruction
|
||||
or
|
||||
operand.getType() instanceof Cpp::ReferenceType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `address` is a use of an SSA variable rooted at `base`, and the
|
||||
|
||||
@@ -6,26 +6,16 @@ private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import SsaInternals as Ssa
|
||||
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.cpp.ir.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step. This relation is only used for local taint flow
|
||||
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
|
||||
* special cases that should only apply to local taint flow.
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// dataflow step
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// taint flow step
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// models-as-data summarized flow for local data flow (i.e. special case for flow
|
||||
// through calls to modeled functions, without relying on global dataflow to join
|
||||
// the dots).
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,11 +24,10 @@ predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
* different objects.
|
||||
*/
|
||||
cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
|
||||
model = ""
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||
or
|
||||
modeledTaintStep(nodeFrom, nodeTo, model)
|
||||
modeledTaintStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
|
||||
// indirection of the pointer arithmetic instruction. This provides flow from `source`
|
||||
@@ -46,22 +35,15 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
|
||||
exists(PointerArithmeticInstruction pai, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
|
||||
) and
|
||||
model = ""
|
||||
)
|
||||
or
|
||||
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// object->field conflation for content that is a `TaintInheritingContent`.
|
||||
exists(DataFlow::ContentSet f |
|
||||
readStep(nodeFrom, f, nodeTo) and
|
||||
f.getAReadContent() instanceof TaintInheritingContent
|
||||
) and
|
||||
model = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,8 +120,8 @@ predicate localExprTaint(Expr e1, Expr e2) {
|
||||
* Holds if the additional step from `src` to `sink` should be included in all
|
||||
* global taint flow configurations.
|
||||
*/
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink, string model) {
|
||||
localAdditionalTaintStep(src, sink, model)
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintStep(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,7 +141,7 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
* Holds if taint can flow from `nodeIn` to `nodeOut` through a call to a
|
||||
* modeled function.
|
||||
*/
|
||||
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string model) {
|
||||
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
|
||||
// Normal taint steps
|
||||
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
call.getStaticCallTarget() = func and
|
||||
@@ -168,8 +150,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
|
||||
nodeIn = callInput(call, modelIn) and nodeOut = callOutput(call, modelOut)
|
||||
or
|
||||
exists(int d | nodeIn = callInput(call, modelIn, d) and nodeOut = callOutput(call, modelOut, d))
|
||||
) and
|
||||
model = "TaintFunction"
|
||||
)
|
||||
or
|
||||
// Taint flow from one argument to another and data flow from an argument to a
|
||||
// return value. This happens in functions like `strcat` and `memcpy`. We
|
||||
@@ -186,8 +167,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
|
||||
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
|
||||
modelMidOut.isParameterDeref(indexMid) and
|
||||
modelMidIn.isParameter(indexMid) and
|
||||
model = "TaintFunction"
|
||||
modelMidIn.isParameter(indexMid)
|
||||
)
|
||||
or
|
||||
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
|
||||
@@ -200,11 +180,9 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
|
||||
indirectArgument.hasAddressOperandAndIndirectionIndex(nodeIn.asOperand(), _) and
|
||||
call.getStaticCallTarget() = func and
|
||||
(
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) and
|
||||
model = "DataFlowFunction"
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut) and
|
||||
model = "TaintFunction"
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
) and
|
||||
nodeOut = callOutput(call, modelOut)
|
||||
)
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.typeflow.TypeFlow
|
||||
|
||||
private module Input implements TypeFlowInput<Location> {
|
||||
/** Holds if `alloc` dynamically allocates a single object. */
|
||||
private predicate isSingleObjectAllocation(AllocationExpr alloc) {
|
||||
// i.e., `new int`;
|
||||
alloc instanceof NewExpr
|
||||
or
|
||||
// i.e., `malloc(sizeof(int))`
|
||||
exists(SizeofTypeOperator sizeOf | sizeOf = alloc.getSizeExpr() |
|
||||
not sizeOf.getTypeOperand().getUnspecifiedType() instanceof ArrayType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i` is the result of a dynamic allocation.
|
||||
*
|
||||
* `isObject` is `true` if the allocation allocated a single object,
|
||||
* and `false` otherwise.
|
||||
*/
|
||||
private predicate isAllocation(Instruction i, boolean isObject) {
|
||||
exists(AllocationExpr alloc | alloc = i.getUnconvertedResultExpression() |
|
||||
if isSingleObjectAllocation(alloc) then isObject = true else isObject = false
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasExactSingleType(Instruction i) {
|
||||
// The address of a variable is always a single object
|
||||
i instanceof VariableAddressInstruction
|
||||
or
|
||||
// A reference always points to a single object
|
||||
i.getResultLanguageType().hasUnspecifiedType(any(ReferenceType rt), false)
|
||||
or
|
||||
// `this` is never an array
|
||||
i instanceof InitializeThisInstruction
|
||||
or
|
||||
// An allocation of a non-array object
|
||||
isAllocation(i, true)
|
||||
}
|
||||
|
||||
private predicate hasExactBufferType(Instruction i) {
|
||||
// Anything with an array type is a buffer
|
||||
i.getResultLanguageType().hasUnspecifiedType(any(ArrayType at), false)
|
||||
or
|
||||
// An allocation expression that we couldn't conclude allocated a single
|
||||
// expression is assigned a buffer type.
|
||||
isAllocation(i, false)
|
||||
}
|
||||
|
||||
private newtype TTypeFlowNode =
|
||||
TInstructionNode(Instruction i) or
|
||||
TFunctionNode(IRFunction func)
|
||||
|
||||
abstract class TypeFlowNode extends TTypeFlowNode {
|
||||
/** Gets a textual representation of this node. */
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Gets the type of this node. This type may not be the most precise
|
||||
* possible type, but will be used as a starting point of the analysis.
|
||||
*/
|
||||
abstract Type getType();
|
||||
|
||||
/** Gets the location of this node. */
|
||||
abstract Location getLocation();
|
||||
|
||||
/** Gets the underlying `Instruction` of this node, if any. */
|
||||
Instruction asInstruction() { none() }
|
||||
|
||||
/** Gets the underlying `IRFunction` of this node, if any. */
|
||||
IRFunction asFunction() { none() }
|
||||
|
||||
/** Holds if the value of this node is always null. */
|
||||
abstract predicate isNullValue();
|
||||
}
|
||||
|
||||
private class InstructionNode extends TypeFlowNode, TInstructionNode {
|
||||
Instruction instr;
|
||||
|
||||
InstructionNode() { this = TInstructionNode(instr) }
|
||||
|
||||
override string toString() { result = instr.toString() }
|
||||
|
||||
override Type getType() {
|
||||
if hasExactSingleType(instr) then result.isSingle() else result.isBuffer()
|
||||
}
|
||||
|
||||
override Location getLocation() { result = instr.getLocation() }
|
||||
|
||||
override Instruction asInstruction() { result = instr }
|
||||
|
||||
override predicate isNullValue() {
|
||||
instr.(ConstantInstruction).getValue() = "0" and
|
||||
instr.getResultIRType() instanceof IRAddressType
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the `TypeFlowNode` corresponding to `i`. */
|
||||
additional InstructionNode instructionNode(Instruction i) { result.asInstruction() = i }
|
||||
|
||||
private class FunctionNode extends TypeFlowNode, TFunctionNode {
|
||||
IRFunction func;
|
||||
|
||||
FunctionNode() { this = TFunctionNode(func) }
|
||||
|
||||
override string toString() { result = func.toString() }
|
||||
|
||||
Instruction getReturnValueInstruction() {
|
||||
result = func.getReturnInstruction().(ReturnValueInstruction).getReturnValue()
|
||||
}
|
||||
|
||||
override Type getType() { result = instructionNode(this.getReturnValueInstruction()).getType() }
|
||||
|
||||
override Location getLocation() { result = func.getLocation() }
|
||||
|
||||
override IRFunction asFunction() { result = func }
|
||||
|
||||
override predicate isNullValue() {
|
||||
instructionNode(this.getReturnValueInstruction()).isNullValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an ultimiate definition of `phi`. That is, an input to `phi` that is
|
||||
* not itself a `PhiInstruction`.
|
||||
*/
|
||||
private Instruction getAnUltimateLocalDefinition(PhiInstruction phi) {
|
||||
result = phi.getAnInput*() and not result instanceof PhiInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function is private (i.e., cannot be accessed outside its
|
||||
* compilation unit). This means we can use a closed-world assumption about
|
||||
* calls to this function.
|
||||
*/
|
||||
private predicate isPrivate(Function func) {
|
||||
// static functions have internal linkage
|
||||
func.isStatic()
|
||||
or
|
||||
// anonymous namespaces have internal linkage
|
||||
func.getNamespace().getParentNamespace*().isAnonymous()
|
||||
or
|
||||
// private member functions are only called internally from inside the class
|
||||
func.(MemberFunction).isPrivate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is an argument for the parameter `p` in a private callable.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate privateParamArg(InitializeParameterInstruction p, Instruction arg) {
|
||||
exists(CallInstruction call, int i, Function func |
|
||||
call.getArgument(pragma[only_bind_into](i)) = arg and
|
||||
func = call.getStaticCallTarget() and
|
||||
func.getParameter(pragma[only_bind_into](i)) = p.getParameter() and
|
||||
isPrivate(func)
|
||||
)
|
||||
}
|
||||
|
||||
predicate joinStep(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
// instruction -> phi
|
||||
getAnUltimateLocalDefinition(n2.asInstruction()) = n1.asInstruction()
|
||||
or
|
||||
// return value -> function
|
||||
n2.(FunctionNode).getReturnValueInstruction() = n1.asInstruction()
|
||||
or
|
||||
// function -> call
|
||||
exists(Function func | func = n1.asFunction().getFunction() |
|
||||
not func.isVirtual() and
|
||||
n2.asInstruction().(CallInstruction).getStaticCallTarget() = func
|
||||
)
|
||||
or
|
||||
// Argument -> parameter where the parameter's enclosing function
|
||||
// is "private".
|
||||
exists(Instruction arg, Instruction p |
|
||||
privateParamArg(p, arg) and
|
||||
n1.asInstruction() = arg and
|
||||
n2.asInstruction() = p
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if knowing whether `i1` points to a single object or buffer implies
|
||||
* knowing whether `i2` points to a single object or buffer.
|
||||
*/
|
||||
private predicate instructionStep(Instruction i1, Instruction i2) {
|
||||
i2.(CopyInstruction).getSourceValue() = i1
|
||||
or
|
||||
i2.(CopyValueInstruction).getSourceValue() = i1
|
||||
or
|
||||
i2.(ConvertInstruction).getUnary() = i1
|
||||
or
|
||||
i2.(CheckedConvertOrNullInstruction).getUnary() = i1
|
||||
or
|
||||
i2.(InheritanceConversionInstruction).getUnary() = i1
|
||||
or
|
||||
i2.(PointerArithmeticInstruction).getLeft() = i1
|
||||
}
|
||||
|
||||
predicate step(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
instructionStep(n1.asInstruction(), n2.asInstruction())
|
||||
}
|
||||
|
||||
predicate isNullValue(TypeFlowNode n) { n.isNullValue() }
|
||||
|
||||
private newtype TType =
|
||||
TSingle() or
|
||||
TBuffer()
|
||||
|
||||
class Type extends TType {
|
||||
string toString() {
|
||||
this.isSingle() and
|
||||
result = "Single"
|
||||
or
|
||||
this.isBuffer() and
|
||||
result = "Buffer"
|
||||
}
|
||||
|
||||
/** Holds if this type is the type that represents a single object. */
|
||||
predicate isSingle() { this = TSingle() }
|
||||
|
||||
/** Holds if this type is the type that represents a buffer. */
|
||||
predicate isBuffer() { this = TBuffer() }
|
||||
|
||||
/**
|
||||
* Gets a super type of this type, if any.
|
||||
*
|
||||
* The type relation is `Single <: Buffer`.
|
||||
*/
|
||||
Type getASupertype() {
|
||||
this.isSingle() and
|
||||
result.isBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
predicate exactTypeBase(TypeFlowNode n, Type t) {
|
||||
exists(Instruction instr | instr = n.asInstruction() |
|
||||
hasExactSingleType(instr) and t.isSingle()
|
||||
or
|
||||
hasExactBufferType(instr) and t.isBuffer()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate upcastCand(TypeFlowNode n, Type t1, Type t2) {
|
||||
exists(TypeFlowNode next |
|
||||
step(n, next)
|
||||
or
|
||||
joinStep(n, next)
|
||||
|
|
||||
n.getType() = t1 and
|
||||
next.getType() = t2 and
|
||||
t1 != t2
|
||||
)
|
||||
}
|
||||
|
||||
private predicate upcast(TypeFlowNode n, Type t1) {
|
||||
exists(Type t2 | upcastCand(n, t1, t2) |
|
||||
// No need for transitive closure since the subtyping relation is just `Single <: Buffer`
|
||||
t1.getASupertype() = t2
|
||||
)
|
||||
}
|
||||
|
||||
predicate typeFlowBaseCand(TypeFlowNode n, Type t) { upcast(n, t) }
|
||||
}
|
||||
|
||||
private module TypeFlow = Make<Location, Input>;
|
||||
|
||||
/**
|
||||
* Holds if `i` is an instruction that computes an address that points to a
|
||||
* single object (as opposed to pointing into a buffer).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate isPointerToSingleObject(Instruction i) {
|
||||
TypeFlow::bestTypeFlow(Input::instructionNode(i), any(Input::Type t | t.isSingle()), _)
|
||||
}
|
||||
@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2, _)
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2, _)
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2, _)
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,5 +41,5 @@ class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration {
|
||||
* Holds if the escape analysis done by SSA construction should be sound. By default, the SSA is
|
||||
* built assuming that no variable's address ever escapes.
|
||||
*/
|
||||
predicate useSoundEscapeAnalysis() { any() }
|
||||
predicate useSoundEscapeAnalysis() { none() }
|
||||
}
|
||||
|
||||
@@ -247,7 +247,8 @@ class Instruction extends Construction::TStageInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
|
||||
cached
|
||||
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
@@ -994,8 +995,9 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
*/
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
|
||||
exists(IRType resultType |
|
||||
resultType = this.getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1007,17 +1009,6 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is a constant value of a pointer type.
|
||||
*/
|
||||
class PointerConstantInstruction extends ConstantInstruction {
|
||||
PointerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is the address of a string literal.
|
||||
*/
|
||||
|
||||
@@ -429,11 +429,6 @@ private module Cached {
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
IRType getInstructionResultIRType(Instruction instr) {
|
||||
result = instr.getResultLanguageType().getIRType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
|
||||
*
|
||||
|
||||
@@ -247,7 +247,8 @@ class Instruction extends Construction::TStageInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
|
||||
cached
|
||||
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
@@ -994,8 +995,9 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
*/
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
|
||||
exists(IRType resultType |
|
||||
resultType = this.getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1007,17 +1009,6 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is a constant value of a pointer type.
|
||||
*/
|
||||
class PointerConstantInstruction extends ConstantInstruction {
|
||||
PointerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is the address of a string literal.
|
||||
*/
|
||||
|
||||
@@ -377,10 +377,6 @@ CppType getInstructionResultType(TStageInstruction instr) {
|
||||
result = getVoidType()
|
||||
}
|
||||
|
||||
IRType getInstructionResultIRType(Instruction instr) {
|
||||
result = instr.getResultLanguageType().getIRType()
|
||||
}
|
||||
|
||||
predicate getInstructionOpcode(Opcode opcode, TStageInstruction instr) {
|
||||
getInstructionTranslatedElement(instr).hasInstruction(opcode, getInstructionTag(instr), _)
|
||||
or
|
||||
|
||||
@@ -89,8 +89,7 @@ newtype TInstructionTag =
|
||||
ImplicitDestructorTag(int index) {
|
||||
exists(Expr e | exists(e.getImplicitDestructorCall(index))) or
|
||||
exists(Stmt s | exists(s.getImplicitDestructorCall(index)))
|
||||
} or
|
||||
CoAwaitBranchTag()
|
||||
}
|
||||
|
||||
class InstructionTag extends TInstructionTag {
|
||||
final string toString() { result = getInstructionTagId(this) }
|
||||
@@ -187,8 +186,6 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
or
|
||||
tag = BoolConversionCompareTag() and result = "BoolConvComp"
|
||||
or
|
||||
tag = ResultCopyTag() and result = "ResultCopy"
|
||||
or
|
||||
tag = LoadTag() and result = "Load" // Implicit load due to lvalue-to-rvalue conversion
|
||||
or
|
||||
tag = CatchTag() and result = "Catch"
|
||||
@@ -266,6 +263,4 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
exists(int index |
|
||||
tag = ImplicitDestructorTag(index) and result = "ImplicitDestructor(" + index + ")"
|
||||
)
|
||||
or
|
||||
tag = CoAwaitBranchTag() and result = "CoAwaitBranch"
|
||||
}
|
||||
|
||||
@@ -128,10 +128,8 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
|
||||
)
|
||||
or
|
||||
// Do not translate implicit destructor calls for unnamed temporary variables that are
|
||||
// conditionally constructed (until we have a mechanism for calling these only when the
|
||||
// temporary's constructor was run)
|
||||
isConditionalTemporaryDestructorCall(expr)
|
||||
// suppress destructors of temporary variables until proper support is added for them.
|
||||
exists(Expr parent | parent.getAnImplicitDestructorCall() = expr)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,6 +150,11 @@ private predicate ignoreExprOnly(Expr expr) {
|
||||
or
|
||||
not translateFunction(getEnclosingFunction(expr)) and
|
||||
not Raw::varHasIRFunc(getEnclosingVariable(expr))
|
||||
or
|
||||
exists(DeleteOrDeleteArrayExpr deleteExpr |
|
||||
// Ignore the destructor call, because the duplicated qualifier breaks control flow.
|
||||
deleteExpr.getDestructorCall() = expr
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,42 +260,6 @@ private predicate usedAsCondition(Expr expr) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasThrowingChild(Expr e) {
|
||||
e = any(ThrowExpr throw).getFullyConverted()
|
||||
or
|
||||
exists(Expr child |
|
||||
e = getRealParent(child) and
|
||||
hasThrowingChild(child)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isInConditionalEvaluation(Expr e) {
|
||||
exists(ConditionalExpr cond |
|
||||
e = cond.getThen().getFullyConverted() and not cond.isTwoOperand()
|
||||
or
|
||||
e = cond.getElse().getFullyConverted()
|
||||
or
|
||||
// If one of the operands throws then the temporaries constructed in either
|
||||
// branch will also be attached to the ternary expression. We suppress
|
||||
// those destructor calls as well.
|
||||
hasThrowingChild([cond.getThen(), cond.getElse()]) and
|
||||
e = cond.getFullyConverted()
|
||||
)
|
||||
or
|
||||
e = any(LogicalAndExpr lae).getRightOperand().getFullyConverted()
|
||||
or
|
||||
e = any(LogicalOrExpr loe).getRightOperand().getFullyConverted()
|
||||
or
|
||||
isInConditionalEvaluation(getRealParent(e))
|
||||
}
|
||||
|
||||
private predicate isConditionalTemporaryDestructorCall(DestructorCall dc) {
|
||||
exists(TemporaryObjectExpr temp |
|
||||
temp = dc.getQualifier().(ReuseExpr).getReusedExpr() and
|
||||
isInConditionalEvaluation(temp)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `conv` is an `InheritanceConversion` that requires a `TranslatedLoad`, despite not being
|
||||
* marked as having an lvalue-to-rvalue conversion.
|
||||
@@ -544,7 +511,8 @@ private module IRDeclarationEntries {
|
||||
* An entity that represents a declaration entry in the database.
|
||||
*
|
||||
* This class exists to work around the fact that `DeclStmt`s in some cases
|
||||
* do not have `DeclarationEntry`s in older databases.
|
||||
* do not have `DeclarationEntry`s. Currently, this is the case for:
|
||||
* - `DeclStmt`s in template instantiations.
|
||||
*
|
||||
* So instead, the IR works with `IRDeclarationEntry`s that synthesize missing
|
||||
* `DeclarationEntry`s when there is no result for `DeclStmt::getDeclarationEntry`.
|
||||
@@ -820,16 +788,6 @@ newtype TTranslatedElement =
|
||||
not ignoreSideEffects(expr) and
|
||||
opcode = getCallSideEffectOpcode(expr)
|
||||
} or
|
||||
// The set of destructors to invoke after a `throw`. These need to be special
|
||||
// cased because the edge kind following a throw is an `ExceptionEdge`, and
|
||||
// we need to make sure that the edge kind is still an `ExceptionEdge` after
|
||||
// all the destructors have run.
|
||||
TTranslatedDestructorsAfterThrow(ThrowExpr throw) {
|
||||
exists(DestructorCall dc |
|
||||
dc = throw.getAnImplicitDestructorCall() and
|
||||
not ignoreExpr(dc)
|
||||
)
|
||||
} or
|
||||
// A precise side effect of an argument to a `Call`
|
||||
TTranslatedArgumentExprSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
|
||||
not ignoreExpr(expr) and
|
||||
|
||||
@@ -90,26 +90,10 @@ abstract class TranslatedExpr extends TranslatedElement {
|
||||
final override TranslatedElement getChild(int id) {
|
||||
result = this.getChildInternal(id)
|
||||
or
|
||||
exists(int destructorIndex |
|
||||
result = this.getImplicitDestructorCall(destructorIndex) and
|
||||
id = this.getFirstDestructorCallIndex() + destructorIndex
|
||||
)
|
||||
}
|
||||
|
||||
final private TranslatedExpr getImplicitDestructorCall(int index) {
|
||||
result.getExpr() = expr.getImplicitDestructorCall(index)
|
||||
}
|
||||
|
||||
final override predicate hasAnImplicitDestructorCall() {
|
||||
exists(this.getImplicitDestructorCall(_))
|
||||
}
|
||||
|
||||
final override int getFirstDestructorCallIndex() {
|
||||
not this.handlesDestructorsExplicitly() and
|
||||
(
|
||||
result = max(int childId | exists(this.getChildInternal(childId))) + 1
|
||||
or
|
||||
not exists(this.getChildInternal(_)) and result = 0
|
||||
exists(int maxChildId, int destructorIndex |
|
||||
maxChildId = max(int childId | exists(this.getChildInternal(childId))) and
|
||||
result.(TranslatedExpr).getExpr() = expr.getImplicitDestructorCall(destructorIndex) and
|
||||
id = maxChildId + 1 + destructorIndex
|
||||
)
|
||||
}
|
||||
|
||||
@@ -411,14 +395,6 @@ class TranslatedLoad extends TranslatedValueCategoryAdjustment, TTranslatedLoad
|
||||
result = this.getOperand().getResult()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate handlesDestructorsExplicitly() {
|
||||
// The class that generates IR for `e` will (implicitly or explicitly)
|
||||
// handle the generation of destructor calls for `e`. Without disabling
|
||||
// destructor call generation here the destructor will get multiple
|
||||
// parents.
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,11 +514,6 @@ class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
|
||||
final override predicate producesExprResult() { any() }
|
||||
|
||||
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
|
||||
|
||||
override predicate handlesDestructorsExplicitly() {
|
||||
// The destructor calls will already have been generated by the translation of `expr`.
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedCommaExpr extends TranslatedNonConstantExpr {
|
||||
@@ -1311,146 +1282,6 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a `co_await` or `co_yield` expression.
|
||||
*
|
||||
* The translation of `x = co_await ...` is essentially:
|
||||
* ```cpp
|
||||
* if (!awaiter.await_ready()) {
|
||||
* awaiter.await_suspend();
|
||||
* }
|
||||
* x = awaiter.await_resume();
|
||||
* ```
|
||||
* where `awaiter` is an object constructed from programmer-supplied
|
||||
* input, and for IR construction purposes these are resolved by the C/C++
|
||||
* front-end.
|
||||
*
|
||||
* See https://en.cppreference.com/w/cpp/language/coroutines#co_await for the
|
||||
* specification on how `awaiter` is obtained.
|
||||
*/
|
||||
abstract private class TranslatedCoExpr extends TranslatedNonConstantExpr {
|
||||
/** Gets the operand of this operation. */
|
||||
abstract Expr getOperand();
|
||||
|
||||
/**
|
||||
* Gets the expression that decides if the enclosing coroutine should be
|
||||
* suspended.
|
||||
*/
|
||||
abstract Expr getAwaitReady();
|
||||
|
||||
/**
|
||||
* Gets the expression that is evaluated when the enclosing coroutine is
|
||||
* suspended.
|
||||
*/
|
||||
abstract Expr getAwaitSuspend();
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the resume point if the enclosing
|
||||
* coroutine was suspended.
|
||||
*/
|
||||
abstract Expr getAwaitResume();
|
||||
|
||||
final override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getTranslatedOperand().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getTranslatedAwaitResume().getALastInstruction()
|
||||
}
|
||||
|
||||
final override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and result = this.getTranslatedOperand()
|
||||
or
|
||||
id = 1 and result = this.getTranslatedAwaitReady()
|
||||
or
|
||||
id = 2 and result = this.getTranslatedAwaitResume()
|
||||
or
|
||||
id = 3 and result = this.getTranslatedAwaitSuspend()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = CoAwaitBranchTag() and
|
||||
(
|
||||
kind instanceof TrueEdge and
|
||||
result = this.getTranslatedAwaitResume().getFirstInstruction(any(GotoEdge goto))
|
||||
or
|
||||
kind instanceof FalseEdge and
|
||||
result = this.getTranslatedAwaitSuspend().getFirstInstruction(any(GotoEdge goto))
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = this.getTranslatedAwaitResume().getResult() }
|
||||
|
||||
final override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getTranslatedOperand() and
|
||||
result = this.getTranslatedAwaitReady().getFirstInstruction(kind)
|
||||
or
|
||||
child = this.getTranslatedAwaitReady() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(CoAwaitBranchTag())
|
||||
or
|
||||
child = this.getTranslatedAwaitSuspend() and
|
||||
result = this.getTranslatedAwaitResume().getFirstInstruction(kind)
|
||||
or
|
||||
child = this.getTranslatedAwaitResume() and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = CoAwaitBranchTag() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = CoAwaitBranchTag() and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = this.getTranslatedAwaitReady().getResult()
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedOperand() {
|
||||
result = getTranslatedExpr(this.getOperand().getFullyConverted())
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedAwaitReady() {
|
||||
result = getTranslatedExpr(this.getAwaitReady().getFullyConverted())
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedAwaitResume() {
|
||||
result = getTranslatedExpr(this.getAwaitResume().getFullyConverted())
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedAwaitSuspend() {
|
||||
result = getTranslatedExpr(this.getAwaitSuspend().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/** IR translation of `co_await`. */
|
||||
class TranslatedCoAwaitExpr extends TranslatedCoExpr {
|
||||
override CoAwaitExpr expr;
|
||||
|
||||
final override Expr getOperand() { result = expr.getOperand() }
|
||||
|
||||
final override Expr getAwaitReady() { result = expr.getAwaitReady() }
|
||||
|
||||
final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }
|
||||
|
||||
final override Expr getAwaitResume() { result = expr.getAwaitResume() }
|
||||
}
|
||||
|
||||
/** IR translation of `co_yield`. */
|
||||
class TranslatedCoYieldxpr extends TranslatedCoExpr {
|
||||
override CoYieldExpr expr;
|
||||
|
||||
final override Expr getOperand() { result = expr.getOperand() }
|
||||
|
||||
final override Expr getAwaitReady() { result = expr.getAwaitReady() }
|
||||
|
||||
final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }
|
||||
|
||||
final override Expr getAwaitResume() { result = expr.getAwaitResume() }
|
||||
}
|
||||
|
||||
abstract class TranslatedConversion extends TranslatedNonConstantExpr {
|
||||
override Conversion expr;
|
||||
|
||||
@@ -2168,13 +1999,6 @@ abstract class TranslatedAllocationSize extends TranslatedExpr, TTranslatedAlloc
|
||||
final override predicate producesExprResult() { none() }
|
||||
|
||||
final override Instruction getResult() { result = this.getInstruction(AllocationSizeTag()) }
|
||||
|
||||
final override predicate handlesDestructorsExplicitly() {
|
||||
// Since the enclosing `TranslatedNewOrNewArrayExpr` (implicitly) handles the destructors
|
||||
// we need to disable the implicit handling here as otherwise the destructors will have
|
||||
// multiple parents
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
TranslatedAllocationSize getTranslatedAllocationSize(NewOrNewArrayExpr newExpr) {
|
||||
@@ -2332,13 +2156,6 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
|
||||
|
||||
final override predicate producesExprResult() { none() }
|
||||
|
||||
final override predicate handlesDestructorsExplicitly() {
|
||||
// Since the enclosing `TranslatedNewOrNewArrayExpr` (implicitly) handles the destructors
|
||||
// we need to disable the implicit handling here as otherwise the destructors will have
|
||||
// multiple parents
|
||||
any()
|
||||
}
|
||||
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = expr.getAllocator()
|
||||
}
|
||||
@@ -2428,11 +2245,7 @@ class TranslatedDeleteOrDeleteArrayExpr extends TranslatedNonConstantExpr, Trans
|
||||
|
||||
final override Type getCallResultType() { result = expr.getType() }
|
||||
|
||||
final override TranslatedExpr getQualifier() {
|
||||
result = getTranslatedExpr(expr.getDestructorCall())
|
||||
}
|
||||
|
||||
final override Instruction getQualifierResult() { none() }
|
||||
final override TranslatedExpr getQualifier() { none() }
|
||||
|
||||
final override predicate hasArguments() {
|
||||
// All deallocator calls have at least one argument.
|
||||
@@ -2447,7 +2260,7 @@ class TranslatedDeleteOrDeleteArrayExpr extends TranslatedNonConstantExpr, Trans
|
||||
final override TranslatedExpr getArgument(int index) {
|
||||
// The only argument we define is the pointer to be deallocated.
|
||||
index = 0 and
|
||||
result = getTranslatedExpr(expr.getExprWithReuse().getFullyConverted())
|
||||
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
|
||||
}
|
||||
|
||||
final override predicate mayThrowException() {
|
||||
@@ -3000,68 +2813,6 @@ class TranslatedReuseExpr extends TranslatedNonConstantExpr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the destructor calls of the parent `TranslatedThrow`.
|
||||
*
|
||||
* This object does not itself generate the destructor calls. Instead, its
|
||||
* children provide the actual calls, and this object ensures that we correctly
|
||||
* exit with an `ExceptionEdge` after executing all the destructor calls.
|
||||
*/
|
||||
class TranslatedDestructorsAfterThrow extends TranslatedElement, TTranslatedDestructorsAfterThrow {
|
||||
ThrowExpr throw;
|
||||
|
||||
TranslatedDestructorsAfterThrow() { this = TTranslatedDestructorsAfterThrow(throw) }
|
||||
|
||||
override string toString() { result = "Destructor calls after throw: " + throw }
|
||||
|
||||
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
|
||||
result.getExpr() = throw.getImplicitDestructorCall(id)
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getChild(0).getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
override ThrowExpr getAst() { result = throw }
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
result = this.getTranslatedImplicitDestructorCall(id)
|
||||
}
|
||||
|
||||
override predicate handlesDestructorsExplicitly() { any() }
|
||||
|
||||
override Declaration getFunction() { result = throw.getEnclosingFunction() }
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
exists(int id | child = this.getChild(id) |
|
||||
// Transition to the next child, if any.
|
||||
result = this.getChild(id + 1).getFirstInstruction(kind)
|
||||
or
|
||||
// And otherwise, exit this element with an exceptional edge
|
||||
not exists(this.getChild(id + 1)) and
|
||||
kind instanceof ExceptionEdge and
|
||||
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedElement getLastChild() {
|
||||
result =
|
||||
this.getTranslatedImplicitDestructorCall(max(int id |
|
||||
exists(throw.getImplicitDestructorCall(id))
|
||||
))
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getLastChild().getALastInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a `throw` expression.
|
||||
*/
|
||||
@@ -3076,22 +2827,13 @@ abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr {
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = ThrowTag() and
|
||||
(
|
||||
result = this.getDestructors().getFirstInstruction(kind)
|
||||
or
|
||||
not exists(this.getDestructors()) and
|
||||
kind instanceof ExceptionEdge and
|
||||
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
|
||||
)
|
||||
kind instanceof ExceptionEdge and
|
||||
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
|
||||
}
|
||||
|
||||
override Instruction getResult() { none() }
|
||||
|
||||
abstract Opcode getThrowOpcode();
|
||||
|
||||
override predicate handlesDestructorsExplicitly() { any() }
|
||||
|
||||
TranslatedDestructorsAfterThrow getDestructors() { result.getAst() = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3103,9 +2845,6 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableIn
|
||||
|
||||
final override TranslatedElement getChildInternal(int id) {
|
||||
result = TranslatedVariableInitialization.super.getChildInternal(id)
|
||||
or
|
||||
id = max(int i | exists(TranslatedVariableInitialization.super.getChildInternal(i))) + 1 and
|
||||
result = this.getDestructors()
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessorInternal(TranslatedElement elem, EdgeKind kind) {
|
||||
@@ -3171,22 +2910,14 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableIn
|
||||
class TranslatedReThrowExpr extends TranslatedThrowExpr {
|
||||
override ReThrowExpr expr;
|
||||
|
||||
override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and
|
||||
result = this.getDestructors()
|
||||
}
|
||||
override TranslatedElement getChildInternal(int id) { none() }
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getInstruction(ThrowTag()) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getDestructors().getALastInstruction()
|
||||
or
|
||||
not this.hasAnImplicitDestructorCall() and
|
||||
result = this.getInstruction(ThrowTag())
|
||||
}
|
||||
override Instruction getALastInstructionInternal() { result = this.getInstruction(ThrowTag()) }
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }
|
||||
|
||||
|
||||
@@ -1501,41 +1501,3 @@ class TranslatedVlaDeclarationStmt extends TranslatedStmt {
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }
|
||||
}
|
||||
|
||||
class TranslatedCoReturnStmt extends TranslatedStmt {
|
||||
override CoReturnStmt stmt;
|
||||
|
||||
private TranslatedExpr getTranslatedOperand() {
|
||||
result = getTranslatedExpr(stmt.getOperand().getFullyConverted())
|
||||
}
|
||||
|
||||
override TranslatedExpr getChildInternal(int id) {
|
||||
id = 0 and
|
||||
result = this.getTranslatedOperand()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getTranslatedOperand().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::NoOp and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getTranslatedOperand() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,8 @@ class Instruction extends Construction::TStageInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
|
||||
cached
|
||||
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
@@ -994,8 +995,9 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
*/
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
|
||||
exists(IRType resultType |
|
||||
resultType = this.getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1007,17 +1009,6 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is a constant value of a pointer type.
|
||||
*/
|
||||
class PointerConstantInstruction extends ConstantInstruction {
|
||||
PointerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is the address of a string literal.
|
||||
*/
|
||||
|
||||
@@ -429,11 +429,6 @@ private module Cached {
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
IRType getInstructionResultIRType(Instruction instr) {
|
||||
result = instr.getResultLanguageType().getIRType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
private import implementations.Allocation
|
||||
private import implementations.Deallocation
|
||||
private import implementations.Fopen
|
||||
private import implementations.Fread
|
||||
private import implementations.Getenv
|
||||
private import implementations.Gets
|
||||
@@ -42,4 +41,3 @@ private import implementations.SqLite3
|
||||
private import implementations.PostgreSql
|
||||
private import implementations.System
|
||||
private import implementations.StructuredExceptionHandling
|
||||
private import implementations.ZMQ
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `fopen` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/** The function `fopen` and friends. */
|
||||
private class Fopen extends Function, AliasFunction, SideEffectFunction {
|
||||
Fopen() {
|
||||
this.hasGlobalOrStdName(["fopen", "fopen_s", "freopen"])
|
||||
or
|
||||
this.hasGlobalName(["_open", "_wfopen", "_fsopen", "_wfsopen", "_wopen"])
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int i) { none() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
// None of the parameters escape
|
||||
this.getParameter(index).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
(
|
||||
this.hasGlobalOrStdName(["fopen", "fopen_s"])
|
||||
or
|
||||
this.hasGlobalName(["_wfopen", "_fsopen", "_wfsopen"])
|
||||
) and
|
||||
i = [0, 1] and
|
||||
buffer = true
|
||||
or
|
||||
this.hasGlobalOrStdName("freopen") and
|
||||
(
|
||||
i = [0, 1] and
|
||||
buffer = true
|
||||
or
|
||||
i = 2 and
|
||||
buffer = false
|
||||
)
|
||||
or
|
||||
this.hasGlobalName(["_open", "_wopen"]) and
|
||||
i = 0 and
|
||||
buffer = true
|
||||
}
|
||||
}
|
||||
@@ -112,21 +112,3 @@ private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunctio
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A model for `getc` and similar functions that are flow sources.
|
||||
*/
|
||||
private class GetcSource extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";;false;getc;;;ReturnValue;remote", ";;false;getwc;;;ReturnValue;remote",
|
||||
";;false;_getc_nolock;;;ReturnValue;remote", ";;false;_getwc_nolock;;;ReturnValue;remote",
|
||||
";;false;getch;;;ReturnValue;local", ";;false;_getch;;;ReturnValue;local",
|
||||
";;false;_getwch;;;ReturnValue;local", ";;false;_getch_nolock;;;ReturnValue;local",
|
||||
";;false;_getwch_nolock;;;ReturnValue;local", ";;false;getchar;;;ReturnValue;local",
|
||||
";;false;getwchar;;;ReturnValue;local", ";;false;_getchar_nolock;;;ReturnValue;local",
|
||||
";;false;_getwchar_nolock;;;ReturnValue;local",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ class IteratorCrementNonMemberOperator extends Operator {
|
||||
}
|
||||
|
||||
private class IteratorCrementNonMemberOperatorModel extends IteratorCrementNonMemberOperator,
|
||||
DataFlowFunction, SideEffectFunction, AliasFunction
|
||||
DataFlowFunction
|
||||
{
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = getIteratorArgumentInput(this, 0) and
|
||||
@@ -131,24 +131,6 @@ private class IteratorCrementNonMemberOperatorModel extends IteratorCrementNonMe
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
// See the comment on `IteratorCrementMemberOperatorModel::hasSpecificWriteSideEffect`
|
||||
// for an explanation of these values.
|
||||
i = 0 and buffer = false and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { none() }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +146,7 @@ class IteratorCrementMemberOperator extends MemberFunction {
|
||||
}
|
||||
|
||||
private class IteratorCrementMemberOperatorModel extends IteratorCrementMemberOperator,
|
||||
DataFlowFunction, TaintFunction, SideEffectFunction, AliasFunction
|
||||
DataFlowFunction, TaintFunction
|
||||
{
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierAddress() and
|
||||
@@ -181,28 +163,6 @@ private class IteratorCrementMemberOperatorModel extends IteratorCrementMemberOp
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
// We have two choices here: either `buffer` must be `true` or `mustWrite`
|
||||
// must be `false` to ensure that the IR alias analysis doesn't think that
|
||||
// `it++` completely override the value of the iterator.
|
||||
// We choose `mustWrite` must be `false`. In that case, the value of
|
||||
// `buffer` isn't super important (it just decides which kind of read side
|
||||
// effect will be emitted).
|
||||
i = -1 and buffer = false and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,7 +332,7 @@ class IteratorAssignArithmeticOperator extends Function {
|
||||
* non-member and member versions, use `IteratorPointerDereferenceOperator`.
|
||||
*/
|
||||
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction, AliasFunction, SideEffectFunction
|
||||
IteratorReferenceFunction
|
||||
{
|
||||
IteratorPointerDereferenceMemberOperator() {
|
||||
this.getClassAndName("operator*") instanceof Iterator
|
||||
@@ -385,18 +345,6 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = -1 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,7 +361,7 @@ class IteratorPointerDereferenceNonMemberOperator extends Operator, IteratorRefe
|
||||
}
|
||||
|
||||
private class IteratorPointerDereferenceNonMemberOperatorModel extends IteratorPointerDereferenceNonMemberOperator,
|
||||
TaintFunction, AliasFunction, SideEffectFunction
|
||||
TaintFunction
|
||||
{
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = getIteratorArgumentInput(this, 0) and
|
||||
@@ -422,18 +370,6 @@ private class IteratorPointerDereferenceNonMemberOperatorModel extends IteratorP
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,71 +420,6 @@ class IteratorAssignmentMemberOperator extends MemberFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A member `operator==` or `operator!=` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorLogicalOperator`.
|
||||
*/
|
||||
class IteratorLogicalMemberOperator extends MemberFunction {
|
||||
IteratorLogicalMemberOperator() {
|
||||
this.getClassAndName(["operator!=", "operator=="]) instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorLogicalMemberOperatorModel extends IteratorLogicalMemberOperator,
|
||||
AliasFunction, SideEffectFunction
|
||||
{
|
||||
override predicate parameterNeverEscapes(int index) { index = [-1, 0] }
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A member `operator==` or `operator!=` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorLogicalOperator`.
|
||||
*/
|
||||
class IteratorLogicalNonMemberOperator extends Function {
|
||||
IteratorLogicalNonMemberOperator() {
|
||||
this.hasName(["operator!=", "operator=="]) and
|
||||
exists(getIteratorArgumentInput(this, 0)) and
|
||||
exists(getIteratorArgumentInput(this, 1))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorLogicalNonMemberOperatorModel extends IteratorLogicalNonMemberOperator,
|
||||
AliasFunction, SideEffectFunction
|
||||
{
|
||||
override predicate parameterNeverEscapes(int index) { index = [0, 1] }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A (member or non-member) `operator==` or `operator!=` function for an iterator type.
|
||||
*/
|
||||
class IteratorLogicalOperator extends Function {
|
||||
IteratorLogicalOperator() {
|
||||
this instanceof IteratorLogicalNonMemberOperator
|
||||
or
|
||||
this instanceof IteratorLogicalMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator=` member function of an iterator class that is not a copy or move assignment
|
||||
* operator.
|
||||
@@ -557,26 +428,12 @@ class IteratorLogicalOperator extends Function {
|
||||
* `operator*` and use their own `operator=` to assign to the container.
|
||||
*/
|
||||
private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMemberOperator,
|
||||
TaintFunction, SideEffectFunction, AliasFunction
|
||||
TaintFunction
|
||||
{
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
// See the comment on `IteratorCrementMemberOperatorModel::hasSpecificWriteSideEffect`
|
||||
// for an explanation of these values.
|
||||
i = -1 and buffer = false and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling the ZeroMQ networking library.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* Remote flow sources.
|
||||
*/
|
||||
private class ZmqSource extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";;false;zmq_recv;;;Argument[*1];remote", ";;false;zmq_recvmsg;;;Argument[*1];remote",
|
||||
";;false;zmq_msg_recv;;;Argument[*0];remote",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote flow sinks.
|
||||
*/
|
||||
private class ZmqSinks extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";;false;zmq_send;;;Argument[*1];remote-sink",
|
||||
";;false;zmq_sendmsg;;;Argument[*1];remote-sink",
|
||||
";;false;zmq_msg_send;;;Argument[*0];remote-sink",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flow steps.
|
||||
*/
|
||||
private class ZmqSummaries extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";;false;zmq_msg_init_data;;;Argument[*1];Argument[*0];taint",
|
||||
";;false;zmq_msg_data;;;Argument[*0];ReturnValue[*];taint",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
import cpp
|
||||
import FunctionInputsAndOutputs
|
||||
import semmle.code.cpp.models.Models
|
||||
import semmle.code.cpp.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A library function that returns data that may be read from a network connection.
|
||||
|
||||
@@ -20,9 +20,6 @@ abstract class RemoteFlowSource extends FlowSource { }
|
||||
/** A data flow source of local user input. */
|
||||
abstract class LocalFlowSource extends FlowSource { }
|
||||
|
||||
/**
|
||||
* A remote data flow source that is defined through a `RemoteFlowSourceFunction` model.
|
||||
*/
|
||||
private class RemoteModelSource extends RemoteFlowSource {
|
||||
string sourceType;
|
||||
|
||||
@@ -37,9 +34,6 @@ private class RemoteModelSource extends RemoteFlowSource {
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A local data flow source that is defined through a `LocalFlowSourceFunction` model.
|
||||
*/
|
||||
private class LocalModelSource extends LocalFlowSource {
|
||||
string sourceType;
|
||||
|
||||
@@ -54,9 +48,6 @@ private class LocalModelSource extends LocalFlowSource {
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A local data flow source that the `argv` parameter to `main`.
|
||||
*/
|
||||
private class ArgvSource extends LocalFlowSource {
|
||||
ArgvSource() {
|
||||
exists(Function main, Parameter argv |
|
||||
@@ -69,33 +60,12 @@ private class ArgvSource extends LocalFlowSource {
|
||||
override string getSourceType() { result = "a command-line argument" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote data flow source that is defined through 'models as data'.
|
||||
*/
|
||||
private class ExternalRemoteFlowSource extends RemoteFlowSource {
|
||||
ExternalRemoteFlowSource() { sourceNode(this, "remote") }
|
||||
|
||||
override string getSourceType() { result = "external" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A local data flow source that is defined through 'models as data'.
|
||||
*/
|
||||
private class ExternalLocalFlowSource extends LocalFlowSource {
|
||||
ExternalLocalFlowSource() { sourceNode(this, "local") }
|
||||
|
||||
override string getSourceType() { result = "external" }
|
||||
}
|
||||
|
||||
/** A remote data flow sink. */
|
||||
abstract class RemoteFlowSink extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this flow sink. */
|
||||
abstract string getSinkType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote flow sink derived from the `RemoteFlowSinkFunction` model.
|
||||
*/
|
||||
private class RemoteParameterSink extends RemoteFlowSink {
|
||||
string sourceType;
|
||||
|
||||
@@ -109,12 +79,3 @@ private class RemoteParameterSink extends RemoteFlowSink {
|
||||
|
||||
override string getSinkType() { result = sourceType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote flow sink defined in a CSV model.
|
||||
*/
|
||||
private class RemoteFlowFromCsvSink extends RemoteFlowSink {
|
||||
RemoteFlowFromCsvSink() { sinkNode(this, "remote-sink") }
|
||||
|
||||
override string getSinkType() { result = "remote flow sink" }
|
||||
}
|
||||
|
||||
@@ -139,7 +139,6 @@ private module ParameterSinks {
|
||||
}
|
||||
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Holds if `n` represents the expression `e`, and `e` is a pointer that is
|
||||
@@ -150,11 +149,11 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
predicate isUse(DataFlow::Node n, Expr e) {
|
||||
isUse0(e) and n.asExpr() = e
|
||||
or
|
||||
exists(DataFlowCall call, InitializeParameterInstruction init |
|
||||
exists(CallInstruction call, InitializeParameterInstruction init |
|
||||
n.asOperand().getDef().getUnconvertedResultExpression() = e and
|
||||
pragma[only_bind_into](init) = ParameterSinks::getAnAlwaysDereferencedParameter() and
|
||||
viableParamArg(call, DataFlow::instructionNode(init), n) and
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) =
|
||||
pragma[only_bind_out](call.asCallInstruction().getStaticCallTarget())
|
||||
pragma[only_bind_out](call.getStaticCallTarget())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,616 @@
|
||||
/**
|
||||
* DEPRECATED: This library has been replaced with a newer version which
|
||||
* provides better performance and precision. Use
|
||||
* `semmle.code.cpp.valuenumbering.GlobalValueNumbering` instead.
|
||||
*
|
||||
* Provides an implementation of Global Value Numbering.
|
||||
* See https://en.wikipedia.org/wiki/Global_value_numbering
|
||||
*
|
||||
* The predicate `globalValueNumber` converts an expression into a `GVN`,
|
||||
* which is an abstract type representing the value of the expression. If
|
||||
* two expressions have the same `GVN` then they compute the same value.
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* void f(int x, int y) {
|
||||
* g(x+y, x+y);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example, both arguments in the call to `g` compute the same value,
|
||||
* so both arguments have the same `GVN`. In other words, we can find
|
||||
* this call with the following query:
|
||||
*
|
||||
* ```
|
||||
* from FunctionCall call, GVN v
|
||||
* where v = globalValueNumber(call.getArgument(0))
|
||||
* and v = globalValueNumber(call.getArgument(1))
|
||||
* select call
|
||||
* ```
|
||||
*
|
||||
* The analysis is conservative, so two expressions might have different
|
||||
* `GVN`s even though the actually always compute the same value. The most
|
||||
* common reason for this is that the analysis cannot prove that there
|
||||
* are no side-effects that might cause the computed value to change.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note to developers: the correctness of this module depends on the
|
||||
* definitions of GVN, globalValueNumber, and analyzableExpr being kept in
|
||||
* sync with each other. If you change this module then make sure that the
|
||||
* change is symmetric across all three.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.controlflow.SSA
|
||||
|
||||
/**
|
||||
* Holds if the result is a control flow node that might change the
|
||||
* value of any global variable. This is used in the implementation
|
||||
* of `GVN_OtherVariable`, because we need to be quite conservative when
|
||||
* we assign a value number to a global variable. For example:
|
||||
*
|
||||
* ```
|
||||
* x = g+1;
|
||||
* dosomething();
|
||||
* y = g+1;
|
||||
* ```
|
||||
*
|
||||
* It is not safe to assign the same value number to both instances
|
||||
* of `g+1` in this example, because the call to `dosomething` might
|
||||
* change the value of `g`.
|
||||
*/
|
||||
private ControlFlowNode nodeWithPossibleSideEffect() {
|
||||
result instanceof Call
|
||||
or
|
||||
// If the lhs of an assignment is not analyzable by SSA, then
|
||||
// we need to treat the assignment as having a possible side-effect.
|
||||
result instanceof Assignment and not result instanceof SsaDefinition
|
||||
or
|
||||
result instanceof CrementOperation and not result instanceof SsaDefinition
|
||||
or
|
||||
exists(LocalVariable v |
|
||||
result = v.getInitializer().getExpr() and not result instanceof SsaDefinition
|
||||
)
|
||||
or
|
||||
result instanceof AsmStmt
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry node of the control flow graph of which `node` is a
|
||||
* member.
|
||||
*/
|
||||
cached
|
||||
private ControlFlowNode getControlFlowEntry(ControlFlowNode node) {
|
||||
result = node.getControlFlowScope().getEntryPoint() and
|
||||
result.getASuccessor*() = node
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control flow edge from `src` to `dst` or
|
||||
* if `dst` is an expression with a possible side-effect. The idea
|
||||
* is to treat side effects as entry points in the control flow
|
||||
* graph so that we can use the dominator tree to find the most recent
|
||||
* side-effect.
|
||||
*/
|
||||
private predicate sideEffectCfg(ControlFlowNode src, ControlFlowNode dst) {
|
||||
src.getASuccessor() = dst
|
||||
or
|
||||
// Add an edge from the entry point to any node that might have a side
|
||||
// effect.
|
||||
dst = nodeWithPossibleSideEffect() and
|
||||
src = getControlFlowEntry(dst)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is the immediate dominator of `node` in
|
||||
* the side-effect CFG.
|
||||
*/
|
||||
private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) =
|
||||
idominance(functionEntry/1, sideEffectCfg/2)(_, dominator, node)
|
||||
|
||||
/**
|
||||
* Gets the most recent side effect. To be more precise, `result` is a
|
||||
* dominator of `node` and no side-effects can occur between `result` and
|
||||
* `node`.
|
||||
*
|
||||
* `sideEffectCFG` has an edge from the function entry to every node with a
|
||||
* side-effect. This means that every node with a side-effect has the
|
||||
* function entry as its immediate dominator. So if node `x` dominates node
|
||||
* `y` then there can be no side effects between `x` and `y` unless `x` is
|
||||
* the function entry. So the optimal choice for `result` has the function
|
||||
* entry as its immediate dominator.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* 000: int f(int a, int b, int *p) {
|
||||
* 001: int r = 0;
|
||||
* 002: if (a) {
|
||||
* 003: if (b) {
|
||||
* 004: sideEffect1();
|
||||
* 005: }
|
||||
* 006: } else {
|
||||
* 007: sideEffect2();
|
||||
* 008: }
|
||||
* 009: if (a) {
|
||||
* 010: r++; // Not a side-effect, because r is an SSA variable.
|
||||
* 011: }
|
||||
* 012: if (b) {
|
||||
* 013: r++; // Not a side-effect, because r is an SSA variable.
|
||||
* 014: }
|
||||
* 015: return *p;
|
||||
* 016: }
|
||||
* ```
|
||||
*
|
||||
* Suppose we want to find the most recent side-effect for the dereference
|
||||
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
|
||||
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
|
||||
* the immediate dominator tree looks like this:
|
||||
*
|
||||
* 000 - 001 - 002 - 003
|
||||
* - 004
|
||||
* - 007
|
||||
* - 009 - 010
|
||||
* - 012 - 013
|
||||
* - 015
|
||||
*
|
||||
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
|
||||
* Therefore, the most recent side effect for line 015 is line 009.
|
||||
*/
|
||||
cached
|
||||
private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
|
||||
exists(ControlFlowNode entry |
|
||||
functionEntry(entry) and
|
||||
iDomEffect(entry, result) and
|
||||
iDomEffect*(result, node)
|
||||
)
|
||||
}
|
||||
|
||||
/** Used to represent the "global value number" of an expression. */
|
||||
cached
|
||||
private newtype GvnBase =
|
||||
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
|
||||
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
|
||||
// If the local variable does not have a defining value, then
|
||||
// we use the SsaDefinition as its global value number.
|
||||
GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
|
||||
mk_UndefinedStackVariable(x, def, _)
|
||||
} or
|
||||
// Variables with no SSA information. As a crude (but safe)
|
||||
// approximation, we use `mostRecentSideEffect` to compute a definition
|
||||
// location for the variable. This ensures that two instances of the same
|
||||
// global variable will only get the same value number if they are
|
||||
// guaranteed to have the same value.
|
||||
GVN_OtherVariable(Variable x, ControlFlowNode dominator) { mk_OtherVariable(x, dominator, _) } or
|
||||
deprecated GVN_FieldAccess(GVN s, Field f) {
|
||||
mk_DotFieldAccess(s, f, _) or
|
||||
mk_PointerFieldAccess_with_deref(s, f, _) or
|
||||
mk_ImplicitThisFieldAccess_with_deref(s, f, _)
|
||||
} or
|
||||
// Dereference a pointer. The value might have changed since the last
|
||||
// time the pointer was dereferenced, so we need to include a definition
|
||||
// location. As a crude (but safe) approximation, we use
|
||||
// `mostRecentSideEffect` to compute a definition location.
|
||||
deprecated GVN_Deref(GVN p, ControlFlowNode dominator) {
|
||||
mk_Deref(p, dominator, _) or
|
||||
mk_PointerFieldAccess(p, _, dominator, _) or
|
||||
mk_ImplicitThisFieldAccess_with_qualifier(p, _, dominator, _)
|
||||
} or
|
||||
GVN_ThisExpr(Function fcn) {
|
||||
mk_ThisExpr(fcn, _) or
|
||||
mk_ImplicitThisFieldAccess(fcn, _, _, _)
|
||||
} or
|
||||
deprecated GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } or
|
||||
deprecated GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
|
||||
deprecated GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } or
|
||||
deprecated GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) {
|
||||
mk_ArrayAccess(x, i, dominator, _)
|
||||
} or
|
||||
// Any expression that is not handled by the cases above is
|
||||
// given a unique number based on the expression itself.
|
||||
GVN_Unanalyzable(Expr e) { not analyzableExpr(e) }
|
||||
|
||||
/**
|
||||
* A Global Value Number. A GVN is an abstract representation of the value
|
||||
* computed by an expression. The relationship between `Expr` and `GVN` is
|
||||
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
|
||||
* expressions can have the same `GVN`. If two expressions have the same
|
||||
* `GVN`, it means that they compute the same value at run time. The `GVN`
|
||||
* is an opaque value, so you cannot deduce what the run-time value of an
|
||||
* expression will be from its `GVN`. The only use for the `GVN` of an
|
||||
* expression is to find other expressions that compute the same value.
|
||||
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
|
||||
*
|
||||
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
|
||||
* displayed in a results list. These work by picking an arbitrary
|
||||
* expression with this `GVN` and using its `toString` and `getLocation`
|
||||
* methods.
|
||||
*/
|
||||
deprecated class GVN extends GvnBase {
|
||||
GVN() { this instanceof GvnBase }
|
||||
|
||||
/** Gets an expression that has this GVN. */
|
||||
Expr getAnExpr() { this = globalValueNumber(result) }
|
||||
|
||||
/** Gets the kind of the GVN. This can be useful for debugging. */
|
||||
string getKind() {
|
||||
if this instanceof GVN_IntConst
|
||||
then result = "IntConst"
|
||||
else
|
||||
if this instanceof GVN_FloatConst
|
||||
then result = "FloatConst"
|
||||
else
|
||||
if this instanceof GVN_UndefinedStackVariable
|
||||
then result = "UndefinedStackVariable"
|
||||
else
|
||||
if this instanceof GVN_OtherVariable
|
||||
then result = "OtherVariable"
|
||||
else
|
||||
if this instanceof GVN_FieldAccess
|
||||
then result = "FieldAccess"
|
||||
else
|
||||
if this instanceof GVN_Deref
|
||||
then result = "Deref"
|
||||
else
|
||||
if this instanceof GVN_ThisExpr
|
||||
then result = "ThisExpr"
|
||||
else
|
||||
if this instanceof GVN_Conversion
|
||||
then result = "Conversion"
|
||||
else
|
||||
if this instanceof GVN_BinaryOp
|
||||
then result = "BinaryOp"
|
||||
else
|
||||
if this instanceof GVN_UnaryOp
|
||||
then result = "UnaryOp"
|
||||
else
|
||||
if this instanceof GVN_ArrayAccess
|
||||
then result = "ArrayAccess"
|
||||
else
|
||||
if this instanceof GVN_Unanalyzable
|
||||
then result = "Unanalyzable"
|
||||
else result = "error"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an example of an expression with this GVN.
|
||||
* This is useful for things like implementing toString().
|
||||
*/
|
||||
private Expr exampleExpr() {
|
||||
// Pick the expression with the minimum source location string. This is
|
||||
// just an arbitrary way to pick an expression with this `GVN`.
|
||||
result = min(Expr e | this = globalValueNumber(e) | e order by e.getLocation().toString())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.exampleExpr().toString() }
|
||||
|
||||
/** Gets the primary location of this element. */
|
||||
Location getLocation() { result = this.exampleExpr().getLocation() }
|
||||
}
|
||||
|
||||
private predicate analyzableIntConst(Expr e) {
|
||||
strictcount(e.getValue().toInt()) = 1 and
|
||||
strictcount(e.getUnspecifiedType()) = 1
|
||||
}
|
||||
|
||||
private predicate mk_IntConst(int val, Type t, Expr e) {
|
||||
analyzableIntConst(e) and
|
||||
val = e.getValue().toInt() and
|
||||
t = e.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private predicate analyzableFloatConst(Expr e) {
|
||||
strictcount(e.getValue().toFloat()) = 1 and
|
||||
strictcount(e.getUnspecifiedType()) = 1 and
|
||||
not analyzableIntConst(e)
|
||||
}
|
||||
|
||||
private predicate mk_FloatConst(float val, Type t, Expr e) {
|
||||
analyzableFloatConst(e) and
|
||||
val = e.getValue().toFloat() and
|
||||
t = e.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private predicate analyzableStackVariable(VariableAccess access) {
|
||||
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
|
||||
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
|
||||
count(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = access
|
||||
|
|
||||
def.getDefiningValue(v).getFullyConverted()
|
||||
) <= 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
// Note: this predicate only has a result if the access has no
|
||||
// defining value. If there is a defining value, then there is no
|
||||
// need to generate a fresh `GVN` for the access because `globalValueNumber`
|
||||
// will follow the chain and use the GVN of the defining value.
|
||||
private predicate mk_UndefinedStackVariable(
|
||||
StackVariable x, SsaDefinition def, VariableAccess access
|
||||
) {
|
||||
analyzableStackVariable(access) and
|
||||
access = def.getAUse(x) and
|
||||
not exists(def.getDefiningValue(x))
|
||||
}
|
||||
|
||||
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getQualifier().getFullyConverted()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_DotFieldAccess(GVN qualifier, Field target, DotFieldAccess access) {
|
||||
analyzableDotFieldAccess(access) and
|
||||
target = access.getTarget() and
|
||||
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getQualifier().getFullyConverted()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_PointerFieldAccess(
|
||||
GVN qualifier, Field target, ControlFlowNode dominator, PointerFieldAccess access
|
||||
) {
|
||||
analyzablePointerFieldAccess(access) and
|
||||
dominator = mostRecentSideEffect(access) and
|
||||
target = access.getTarget() and
|
||||
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
/**
|
||||
* `obj->field` is equivalent to `(*obj).field`, so we need to wrap an
|
||||
* extra `GVN_Deref` around the qualifier.
|
||||
*/
|
||||
deprecated private predicate mk_PointerFieldAccess_with_deref(
|
||||
GVN new_qualifier, Field target, PointerFieldAccess access
|
||||
) {
|
||||
exists(GVN qualifier, ControlFlowNode dominator |
|
||||
mk_PointerFieldAccess(qualifier, target, dominator, access) and
|
||||
new_qualifier = GVN_Deref(qualifier, dominator)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getEnclosingFunction()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_ImplicitThisFieldAccess(
|
||||
Function fcn, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
|
||||
) {
|
||||
analyzableImplicitThisFieldAccess(access) and
|
||||
dominator = mostRecentSideEffect(access) and
|
||||
target = access.getTarget() and
|
||||
fcn = access.getEnclosingFunction()
|
||||
}
|
||||
|
||||
deprecated private predicate mk_ImplicitThisFieldAccess_with_qualifier(
|
||||
GVN qualifier, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
|
||||
) {
|
||||
exists(Function fcn |
|
||||
mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and
|
||||
qualifier = GVN_ThisExpr(fcn)
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_ImplicitThisFieldAccess_with_deref(
|
||||
GVN new_qualifier, Field target, ImplicitThisFieldAccess access
|
||||
) {
|
||||
exists(GVN qualifier, ControlFlowNode dominator |
|
||||
mk_ImplicitThisFieldAccess_with_qualifier(qualifier, target, dominator, access) and
|
||||
new_qualifier = GVN_Deref(qualifier, dominator)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `access` is an access of a variable that does
|
||||
* not have SSA information. (For example, because the variable
|
||||
* is global.)
|
||||
*/
|
||||
private predicate analyzableOtherVariable(VariableAccess access) {
|
||||
not access instanceof FieldAccess and
|
||||
not exists(SsaDefinition def | access = def.getAUse(_)) and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_OtherVariable(Variable x, ControlFlowNode dominator, VariableAccess access) {
|
||||
analyzableOtherVariable(access) and
|
||||
x = access.getTarget() and
|
||||
dominator = mostRecentSideEffect(access)
|
||||
}
|
||||
|
||||
private predicate analyzableConversion(Conversion conv) {
|
||||
strictcount(conv.getUnspecifiedType()) = 1 and
|
||||
strictcount(conv.getExpr()) = 1 and
|
||||
not analyzableConst(conv)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_Conversion(Type t, GVN child, Conversion conv) {
|
||||
analyzableConversion(conv) and
|
||||
t = conv.getUnspecifiedType() and
|
||||
child = globalValueNumber(conv.getExpr())
|
||||
}
|
||||
|
||||
private predicate analyzableBinaryOp(BinaryOperation op) {
|
||||
op.isPure() and
|
||||
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getOperator()) = 1 and
|
||||
not analyzableConst(op)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_BinaryOp(GVN lhs, GVN rhs, string opname, BinaryOperation op) {
|
||||
analyzableBinaryOp(op) and
|
||||
lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and
|
||||
rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and
|
||||
opname = op.getOperator()
|
||||
}
|
||||
|
||||
private predicate analyzableUnaryOp(UnaryOperation op) {
|
||||
not op instanceof PointerDereferenceExpr and
|
||||
op.isPure() and
|
||||
strictcount(op.getOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getOperator()) = 1 and
|
||||
not analyzableConst(op)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) {
|
||||
analyzableUnaryOp(op) and
|
||||
child = globalValueNumber(op.getOperand().getFullyConverted()) and
|
||||
opname = op.getOperator()
|
||||
}
|
||||
|
||||
private predicate analyzableThisExpr(ThisExpr thisExpr) {
|
||||
strictcount(thisExpr.getEnclosingFunction()) = 1 and
|
||||
not analyzableConst(thisExpr)
|
||||
}
|
||||
|
||||
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
|
||||
analyzableThisExpr(thisExpr) and
|
||||
fcn = thisExpr.getEnclosingFunction()
|
||||
}
|
||||
|
||||
private predicate analyzableArrayAccess(ArrayExpr ae) {
|
||||
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
|
||||
strictcount(ae.getArrayOffset().getFullyConverted()) = 1 and
|
||||
strictcount(mostRecentSideEffect(ae)) = 1 and
|
||||
not analyzableConst(ae)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_ArrayAccess(
|
||||
GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae
|
||||
) {
|
||||
analyzableArrayAccess(ae) and
|
||||
base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and
|
||||
offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and
|
||||
dominator = mostRecentSideEffect(ae)
|
||||
}
|
||||
|
||||
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
|
||||
strictcount(deref.getOperand().getFullyConverted()) = 1 and
|
||||
strictcount(mostRecentSideEffect(deref)) = 1 and
|
||||
not analyzableConst(deref)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) {
|
||||
analyzablePointerDereferenceExpr(deref) and
|
||||
p = globalValueNumber(deref.getOperand().getFullyConverted()) and
|
||||
dominator = mostRecentSideEffect(deref)
|
||||
}
|
||||
|
||||
/** Gets the global value number of expression `e`. */
|
||||
cached
|
||||
deprecated GVN globalValueNumber(Expr e) {
|
||||
exists(int val, Type t |
|
||||
mk_IntConst(val, t, e) and
|
||||
result = GVN_IntConst(val, t)
|
||||
)
|
||||
or
|
||||
exists(float val, Type t |
|
||||
mk_FloatConst(val, t, e) and
|
||||
result = GVN_FloatConst(val, t)
|
||||
)
|
||||
or
|
||||
// Local variable with a defining value.
|
||||
exists(StackVariable x, SsaDefinition def |
|
||||
analyzableStackVariable(e) and
|
||||
e = def.getAUse(x) and
|
||||
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
|
||||
)
|
||||
or
|
||||
// Local variable without a defining value.
|
||||
exists(StackVariable x, SsaDefinition def |
|
||||
mk_UndefinedStackVariable(x, def, e) and
|
||||
result = GVN_UndefinedStackVariable(x, def)
|
||||
)
|
||||
or
|
||||
// Variable with no SSA information.
|
||||
exists(Variable x, ControlFlowNode dominator |
|
||||
mk_OtherVariable(x, dominator, e) and
|
||||
result = GVN_OtherVariable(x, dominator)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_DotFieldAccess(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_PointerFieldAccess_with_deref(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(Function fcn |
|
||||
mk_ThisExpr(fcn, e) and
|
||||
result = GVN_ThisExpr(fcn)
|
||||
)
|
||||
or
|
||||
exists(Type t, GVN child |
|
||||
mk_Conversion(t, child, e) and
|
||||
result = GVN_Conversion(t, child)
|
||||
)
|
||||
or
|
||||
exists(GVN lhs, GVN rhs, string opname |
|
||||
mk_BinaryOp(lhs, rhs, opname, e) and
|
||||
result = GVN_BinaryOp(lhs, rhs, opname)
|
||||
)
|
||||
or
|
||||
exists(GVN child, string opname |
|
||||
mk_UnaryOp(child, opname, e) and
|
||||
result = GVN_UnaryOp(child, opname)
|
||||
)
|
||||
or
|
||||
exists(GVN x, GVN i, ControlFlowNode dominator |
|
||||
mk_ArrayAccess(x, i, dominator, e) and
|
||||
result = GVN_ArrayAccess(x, i, dominator)
|
||||
)
|
||||
or
|
||||
exists(GVN p, ControlFlowNode dominator |
|
||||
mk_Deref(p, dominator, e) and
|
||||
result = GVN_Deref(p, dominator)
|
||||
)
|
||||
or
|
||||
not analyzableExpr(e) and result = GVN_Unanalyzable(e)
|
||||
}
|
||||
|
||||
private predicate analyzableConst(Expr e) {
|
||||
analyzableIntConst(e) or
|
||||
analyzableFloatConst(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression is explicitly handled by `globalValueNumber`.
|
||||
* Unanalyzable expressions still need to be given a global value number,
|
||||
* but it will be a unique number that is not shared with any other
|
||||
* expression.
|
||||
*/
|
||||
private predicate analyzableExpr(Expr e) {
|
||||
analyzableConst(e) or
|
||||
analyzableStackVariable(e) or
|
||||
analyzableDotFieldAccess(e) or
|
||||
analyzablePointerFieldAccess(e) or
|
||||
analyzableImplicitThisFieldAccess(e) or
|
||||
analyzableOtherVariable(e) or
|
||||
analyzableConversion(e) or
|
||||
analyzableBinaryOp(e) or
|
||||
analyzableUnaryOp(e) or
|
||||
analyzableThisExpr(e) or
|
||||
analyzableArrayAccess(e) or
|
||||
analyzablePointerDereferenceExpr(e)
|
||||
}
|
||||
@@ -1,15 +1,3 @@
|
||||
## 0.9.11
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Uncontrolled data used in path expression" query (`cpp/path-injection`) query produces fewer near-duplicate results.
|
||||
* The "Global variable may be used before initialization" query (`cpp/global-use-before-init`) no longer raises an alert on global variables that are initialized when they are declared.
|
||||
* The "Inconsistent null check of pointer" query (`cpp/inconsistent-nullness-testing`) query no longer raises an alert when the guarded check is in a macro expansion.
|
||||
|
||||
## 0.9.10
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.9
|
||||
|
||||
### New Queries
|
||||
|
||||
@@ -37,5 +37,6 @@ where
|
||||
DoubleFree::flowPath(source, sink) and
|
||||
isFree(source.getNode(), _, _, dealloc) and
|
||||
isFree(sink.getNode(), e2)
|
||||
select sink.getNode(), source, sink, "Memory pointed to by $@ may already have been freed by $@.",
|
||||
e2, e2.toString(), dealloc, dealloc.toString()
|
||||
select sink.getNode(), source, sink,
|
||||
"Memory pointed to by '" + e2.toString() + "' may already have been freed by $@.", dealloc,
|
||||
dealloc.toString()
|
||||
|
||||
@@ -31,9 +31,7 @@ predicate useFunc(GlobalVariable v, Function f) {
|
||||
}
|
||||
|
||||
predicate uninitialisedBefore(GlobalVariable v, Function f) {
|
||||
f.hasGlobalName("main") and
|
||||
not initialisedAtDeclaration(v) and
|
||||
not isStdlibVariable(v)
|
||||
f.hasGlobalName("main")
|
||||
or
|
||||
exists(Call call, Function g |
|
||||
uninitialisedBefore(v, g) and
|
||||
@@ -100,15 +98,10 @@ predicate callReaches(Call call, ControlFlowNode successor) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `v` has an initializer. */
|
||||
predicate initialisedAtDeclaration(GlobalVariable v) { exists(v.getInitializer()) }
|
||||
|
||||
/** Holds if `v` is a global variable that does not need to be initialized. */
|
||||
predicate isStdlibVariable(GlobalVariable v) { v.hasGlobalName(["stdin", "stdout", "stderr"]) }
|
||||
|
||||
from GlobalVariable v, Function f
|
||||
where
|
||||
uninitialisedBefore(v, f) and
|
||||
useFunc(v, f)
|
||||
select f, "The variable $@ is used in this function but may not be initialized when it is called.",
|
||||
v, v.getName()
|
||||
select f,
|
||||
"The variable '" + v.getName() +
|
||||
" is used in this function but may not be initialized when it is called."
|
||||
|
||||
@@ -15,8 +15,6 @@ import cpp
|
||||
from StackVariable v, ControlFlowNode def, VariableAccess checked, VariableAccess unchecked
|
||||
where
|
||||
checked = v.getAnAccess() and
|
||||
// The check can often be in a macro for handling exception
|
||||
not checked.isInMacroExpansion() and
|
||||
dereferenced(checked) and
|
||||
unchecked = v.getAnAccess() and
|
||||
dereferenced(unchecked) and
|
||||
|
||||
@@ -14,10 +14,5 @@ predicate hasDuplicateFunctionEntryPointLocation(Function func) {
|
||||
|
||||
predicate hasDuplicateFunctionEntryPoint(Function func) { count(func.getEntryPoint()) > 1 }
|
||||
|
||||
predicate hasDuplicateDeclarationEntry(DeclStmt stmt, int i) {
|
||||
strictcount(stmt.getDeclarationEntry(i)) > 1
|
||||
}
|
||||
|
||||
select count(Function f | hasDuplicateFunctionEntryPoint(f)) as duplicateFunctionEntryPoint,
|
||||
count(Function f | hasDuplicateFunctionEntryPointLocation(f)) as duplicateFunctionEntryPointLocation,
|
||||
count(DeclStmt stmt, int i | hasDuplicateDeclarationEntry(stmt, i)) as duplicateDeclarationEntry
|
||||
select count(Function f | hasDuplicateFunctionEntryPoint(f) | f) as duplicateFunctionEntryPoint,
|
||||
count(Function f | hasDuplicateFunctionEntryPointLocation(f) | f) as duplicateFunctionEntryPointLocation
|
||||
|
||||
@@ -88,11 +88,6 @@ module TaintedPathConfig implements DataFlow::ConfigSig {
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) {
|
||||
// make sinks barriers so that we only report the closest instance
|
||||
isSink(node)
|
||||
}
|
||||
}
|
||||
|
||||
module TaintedPath = TaintTracking::Global<TaintedPathConfig>;
|
||||
|
||||
@@ -16,7 +16,6 @@ import cpp
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.implementations.Memset
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import ExposedSystemData::PathGraph
|
||||
import SystemData
|
||||
|
||||
@@ -24,11 +23,11 @@ module ExposedSystemDataConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source = any(SystemData sd).getAnExpr() }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof RemoteFlowSink
|
||||
or
|
||||
// workaround for cases where the sink contains the tainted thing as a child; this could
|
||||
// probably be handled better with taint inheriting content or similar modeling.
|
||||
exists(RemoteFlowSink sinkNode | sinkNode.asIndirectExpr().getAChild*() = sink.asIndirectExpr())
|
||||
exists(FunctionCall fc, FunctionInput input, int arg |
|
||||
fc.getTarget().(RemoteFlowSinkFunction).hasRemoteFlowSink(input, _) and
|
||||
input.isParameterDeref(arg) and
|
||||
fc.getArgument(arg).getAChild*() = sink.asIndirectExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
|
||||
@@ -35,7 +35,7 @@ module XxeConfig implements DataFlow::StateConfigSig {
|
||||
) {
|
||||
// create additional flow steps for `XxeFlowStateTransformer`s
|
||||
state2 = node2.asIndirectExpr().(XxeFlowStateTransformer).transform(state1) and
|
||||
DataFlow::simpleLocalFlowStep(node1, node2, _)
|
||||
DataFlow::simpleLocalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState flowstate) {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.9.10
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,7 +0,0 @@
|
||||
## 0.9.11
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Uncontrolled data used in path expression" query (`cpp/path-injection`) query produces fewer near-duplicate results.
|
||||
* The "Global variable may be used before initialization" query (`cpp/global-use-before-init`) no longer raises an alert on global variables that are initialized when they are declared.
|
||||
* The "Inconsistent null check of pointer" query (`cpp/inconsistent-nullness-testing`) query no longer raises an alert when the guarded check is in a macro expansion.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.11
|
||||
lastReleaseVersion: 0.9.9
|
||||
|
||||
@@ -24,7 +24,7 @@ predicate exprMayBeString(Expr exp) {
|
||||
fctmp.getAnArgument().(VariableAccess).getTarget() = exp.(VariableAccess).getTarget() or
|
||||
globalValueNumber(fctmp.getAnArgument()) = globalValueNumber(exp)
|
||||
) and
|
||||
fctmp.getTarget().hasName(["strlen", "strcat", "strncat", "strcpy", "sprintf", "printf"])
|
||||
fctmp.getTarget().hasName(["strlen", "strcat", "strncat", "strcpy", "sptintf", "printf"])
|
||||
)
|
||||
or
|
||||
exists(AssignExpr astmp |
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* external/cwe/cwe-664
|
||||
*/
|
||||
|
||||
// IMPORTANT: This query does not currently find anything since it relies on extractor and analysis improvements that hasn't yet been released
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
@@ -18,20 +19,26 @@ import semmle.code.cpp.models.implementations.StdContainer
|
||||
import semmle.code.cpp.models.implementations.StdMap
|
||||
import semmle.code.cpp.models.implementations.Iterator
|
||||
|
||||
private predicate tempToDestructorSink(DataFlow::Node sink, CallInstruction call) {
|
||||
call = sink.asOperand().(ThisArgumentOperand).getCall() and
|
||||
call.getStaticCallTarget() instanceof Destructor
|
||||
/**
|
||||
* A configuration to track flow from a temporary variable to the qualifier of
|
||||
* a destructor call
|
||||
*/
|
||||
module TempToDestructorConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asOperand().(ThisArgumentOperand).getCall().getStaticCallTarget() instanceof Destructor
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `pun` is the post-update node of the qualifier of `Call`. */
|
||||
private predicate isPostUpdateOfQualifier(CallInstruction call, DataFlow::PostUpdateNode pun) {
|
||||
call.getThisArgumentOperand() = pun.getPreUpdateNode().asOperand()
|
||||
}
|
||||
module TempToDestructorFlow = DataFlow::Global<TempToDestructorConfig>;
|
||||
|
||||
/**
|
||||
* Gets a `DataFlow::Node` that represents a temporary that will be destroyed
|
||||
* by a call to a destructor when `n` is destroyed, or a `DataFlow::Node` that
|
||||
* will transitively be destroyed by a call to a destructor.
|
||||
* by a call to a destructor, or a `DataFlow::Node` that will transitively be
|
||||
* destroyed by a call to a destructor.
|
||||
*
|
||||
* For the latter case, consider something like:
|
||||
* ```
|
||||
@@ -43,25 +50,22 @@ private predicate isPostUpdateOfQualifier(CallInstruction call, DataFlow::PostUp
|
||||
* destroyed by a call to `std::vector<std::vector<int>>::~vector`,
|
||||
* and thus the result of `get_2d_vector()[0]` is also an invalid reference.
|
||||
*/
|
||||
DataFlow::Node getADestroyedNode(DataFlow::Node n) {
|
||||
// Case 1: The pointer that goes into the destructor call is destroyed
|
||||
exists(CallInstruction destructorCall |
|
||||
tempToDestructorSink(n, destructorCall) and
|
||||
isPostUpdateOfQualifier(destructorCall, result)
|
||||
)
|
||||
or
|
||||
// Case 2: Anything that was derived from the temporary that is now destroyed
|
||||
// is also destroyed.
|
||||
exists(CallInstruction call |
|
||||
result.asInstruction() = call and
|
||||
DataFlow::localFlow(DataFlow::operandNode(call.getThisArgumentOperand()), n)
|
||||
|
|
||||
call.getStaticCallTarget() instanceof StdSequenceContainerAt or
|
||||
call.getStaticCallTarget() instanceof StdMapAt
|
||||
DataFlow::Node getADestroyedNode() {
|
||||
exists(TempToDestructorFlow::PathNode destroyedTemp | destroyedTemp.isSource() |
|
||||
result = destroyedTemp.getNode()
|
||||
or
|
||||
exists(CallInstruction call |
|
||||
result.asInstruction() = call and
|
||||
DataFlow::localFlow(destroyedTemp.getNode(),
|
||||
DataFlow::operandNode(call.getThisArgumentOperand()))
|
||||
|
|
||||
call.getStaticCallTarget() instanceof StdSequenceContainerAt or
|
||||
call.getStaticCallTarget() instanceof StdMapAt
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate destroyedToBeginSink(DataFlow::Node sink, FunctionCall fc) {
|
||||
predicate isSinkImpl(DataFlow::Node sink, FunctionCall fc) {
|
||||
exists(CallInstruction call |
|
||||
call = sink.asOperand().(ThisArgumentOperand).getCall() and
|
||||
fc = call.getUnconvertedResultExpression() and
|
||||
@@ -70,39 +74,12 @@ predicate destroyedToBeginSink(DataFlow::Node sink, FunctionCall fc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration to track flow from a temporary variable to the qualifier of
|
||||
* a destructor call, and subsequently to a qualifier of a call to `begin` or
|
||||
* `end`.
|
||||
* Flow from any destroyed object to the qualifier of a `begin` or `end` call
|
||||
*/
|
||||
module Config implements DataFlow::StateConfigSig {
|
||||
newtype FlowState =
|
||||
additional TempToDestructor() or
|
||||
additional DestroyedToBegin(DataFlow::Node n) {
|
||||
exists(DataFlow::Node thisOperand |
|
||||
tempToDestructorSink(thisOperand, _) and
|
||||
n = getADestroyedNode(thisOperand)
|
||||
)
|
||||
}
|
||||
module DestroyedToBeginConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source = getADestroyedNode() }
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable and
|
||||
state = TempToDestructor()
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
tempToDestructorSink(node1, _) and
|
||||
state1 = TempToDestructor() and
|
||||
state2 = DestroyedToBegin(node2) and
|
||||
node2 = getADestroyedNode(node1)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
// Note: This is a non-trivial cartesian product!
|
||||
// Hopefully, both of these sets are quite small in practice
|
||||
destroyedToBeginSink(sink, _) and state instanceof DestroyedToBegin
|
||||
}
|
||||
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
||||
|
||||
DataFlow::FlowFeature getAFeature() {
|
||||
// By blocking argument-to-parameter flow we ensure that we don't enter a
|
||||
@@ -119,11 +96,8 @@ module Config implements DataFlow::StateConfigSig {
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = DataFlow::GlobalWithState<Config>;
|
||||
module DestroyedToBeginFlow = DataFlow::Global<DestroyedToBeginConfig>;
|
||||
|
||||
from Flow::PathNode source, Flow::PathNode sink, FunctionCall beginOrEnd, DataFlow::Node mid
|
||||
where
|
||||
Flow::flowPath(source, sink) and
|
||||
destroyedToBeginSink(sink.getNode(), beginOrEnd) and
|
||||
sink.getState() = Config::DestroyedToBegin(mid)
|
||||
select mid, "This object is destroyed before $@ is called.", beginOrEnd, beginOrEnd.toString()
|
||||
from DataFlow::Node source, DataFlow::Node sink, FunctionCall beginOrEnd
|
||||
where DestroyedToBeginFlow::flow(source, sink) and isSinkImpl(sink, beginOrEnd)
|
||||
select source, "This object is destroyed before $@ is called.", beginOrEnd, beginOrEnd.toString()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.9.11
|
||||
version: 0.9.9
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -426,14 +426,11 @@ DestructorCall.cpp:
|
||||
# 12| getQualifier(): [VariableAccess] c
|
||||
# 12| Type = [PointerType] C *
|
||||
# 12| ValueCategory = prvalue(load)
|
||||
# 12| getExprWithReuse(): [ReuseExpr] reuse of c
|
||||
# 12| Type = [PointerType] C *
|
||||
# 12| ValueCategory = prvalue
|
||||
# 13| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 13| getExpr(): [DeleteExpr] delete
|
||||
# 13| Type = [VoidType] void
|
||||
# 13| ValueCategory = prvalue
|
||||
# 13| getExprWithReuse(): [VariableAccess] d
|
||||
# 13| getExpr(): [VariableAccess] d
|
||||
# 13| Type = [PointerType] D *
|
||||
# 13| ValueCategory = prvalue(load)
|
||||
# 14| getStmt(2): [ReturnStmt] return ...
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
| test.cpp:15:8:15:11 | Load: aptr | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:19:8:19:8 | Load: a | VNLength(Load: count) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:21:8:21:8 | Load: a | VNLength(Load: count) | -1 | ZeroOffset | 0 |
|
||||
| test.cpp:23:8:23:8 | Load: a | VNLength(Load: count) | 1 | ZeroOffset | 0 |
|
||||
| test.cpp:27:8:27:8 | Load: c | VNLength(Load: count) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:28:8:28:24 | Convert: (unsigned char *)... | VNLength(Load: count) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:30:8:30:8 | Load: v | VNLength(Load: count) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:19:8:19:8 | Load: a | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:21:8:21:8 | Load: a | VNLength(Chi: ptr) | -1 | ZeroOffset | 0 |
|
||||
| test.cpp:23:8:23:8 | Load: a | VNLength(Chi: ptr) | 1 | ZeroOffset | 0 |
|
||||
| test.cpp:27:8:27:8 | Load: c | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:28:8:28:24 | Convert: (unsigned char *)... | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:30:8:30:8 | Load: v | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:34:8:34:12 | Convert: array to pointer conversion | ZeroLength | 100 | ZeroOffset | 0 |
|
||||
| test.cpp:37:10:37:10 | Load: b | VNLength(Load: count) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:37:10:37:10 | Load: b | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
|
||||
| test.cpp:44:8:44:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 2 |
|
||||
| test.cpp:53:10:53:10 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 2 |
|
||||
| test.cpp:56:10:56:10 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 3 |
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
| inline_assembly.c:10:3:10:7 | Store: ... = ... | positive strictlyPositive |
|
||||
| inline_assembly.c:10:7:10:7 | Constant: (unsigned int)... | positive strictlyPositive |
|
||||
| inline_assembly.c:12:32:12:32 | Load: y | positive strictlyPositive |
|
||||
| inline_assembly.c:21:29:21:29 | Load: x | positive |
|
||||
| inline_assembly.c:21:32:21:32 | Load: y | positive |
|
||||
| inline_assembly.c:21:32:21:32 | Load: y | positive strictlyPositive |
|
||||
| minmax.c:16:9:16:10 | Constant: 1 | positive strictlyPositive |
|
||||
| minmax.c:16:9:16:10 | Store: 1 | positive strictlyPositive |
|
||||
| minmax.c:16:16:16:17 | Constant: 2 | positive strictlyPositive |
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
edges
|
||||
| test.cpp:22:27:22:30 | **argv | test.cpp:23:20:23:26 | *access to array | provenance | |
|
||||
| test.cpp:23:20:23:26 | *access to array | test.cpp:29:13:29:20 | *filePath | provenance | |
|
||||
| test.cpp:22:27:22:30 | **argv | test.cpp:29:13:29:20 | *filePath | provenance | |
|
||||
nodes
|
||||
| test.cpp:22:27:22:30 | **argv | semmle.label | **argv |
|
||||
| test.cpp:23:20:23:26 | *access to array | semmle.label | *access to array |
|
||||
| test.cpp:29:13:29:20 | *filePath | semmle.label | *filePath |
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
edges
|
||||
| test.cpp:22:17:22:21 | ... * ... | test.cpp:22:17:22:21 | ... * ... | provenance | |
|
||||
| test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 | provenance | |
|
||||
| test.cpp:37:24:37:27 | size | test.cpp:37:46:37:49 | size | provenance | |
|
||||
| test.cpp:45:36:45:40 | ... * ... | test.cpp:37:24:37:27 | size | provenance | |
|
||||
@@ -8,7 +7,6 @@ nodes
|
||||
| test.cpp:15:31:15:35 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:19:34:19:38 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:22:17:22:21 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:22:17:22:21 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:23:33:23:37 | size1 | semmle.label | size1 |
|
||||
| test.cpp:30:18:30:32 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:31:18:31:32 | ... * ... | semmle.label | ... * ... |
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
edges
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:4:17:4:22 | call to malloc | provenance | |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:6:9:6:11 | arr | provenance | |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:10:9:10:11 | arr | provenance | |
|
||||
| test.cpp:19:9:19:16 | *mk_array [p] | test.cpp:28:19:28:26 | call to mk_array [p] | provenance | |
|
||||
@@ -7,9 +6,7 @@ edges
|
||||
| test.cpp:21:5:21:7 | *arr [post update] [p] | test.cpp:22:5:22:7 | *arr [p] | provenance | |
|
||||
| test.cpp:21:5:21:24 | ... = ... | test.cpp:21:5:21:7 | *arr [post update] [p] | provenance | |
|
||||
| test.cpp:21:13:21:18 | call to malloc | test.cpp:21:5:21:24 | ... = ... | provenance | |
|
||||
| test.cpp:22:5:22:7 | *arr [p] | test.cpp:24:12:24:14 | arr [p] | provenance | |
|
||||
| test.cpp:24:12:24:14 | arr [p] | test.cpp:19:9:19:16 | *mk_array [p] | provenance | |
|
||||
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:28:19:28:26 | call to mk_array [p] | provenance | |
|
||||
| test.cpp:22:5:22:7 | *arr [p] | test.cpp:19:9:19:16 | *mk_array [p] | provenance | |
|
||||
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:31:9:31:11 | *arr [p] | provenance | |
|
||||
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:35:9:35:11 | *arr [p] | provenance | |
|
||||
| test.cpp:31:9:31:11 | *arr [p] | test.cpp:31:13:31:13 | p | provenance | |
|
||||
@@ -31,9 +28,7 @@ edges
|
||||
| test.cpp:69:5:69:7 | *arr [post update] [p] | test.cpp:70:5:70:7 | *arr [p] | provenance | |
|
||||
| test.cpp:69:5:69:25 | ... = ... | test.cpp:69:5:69:7 | *arr [post update] [p] | provenance | |
|
||||
| test.cpp:69:14:69:19 | call to malloc | test.cpp:69:5:69:25 | ... = ... | provenance | |
|
||||
| test.cpp:70:5:70:7 | *arr [p] | test.cpp:72:12:72:14 | *arr [p] | provenance | |
|
||||
| test.cpp:72:12:72:14 | *arr [p] | test.cpp:67:10:67:19 | **mk_array_p [p] | provenance | |
|
||||
| test.cpp:76:20:76:29 | *call to mk_array_p [p] | test.cpp:76:20:76:29 | *call to mk_array_p [p] | provenance | |
|
||||
| test.cpp:70:5:70:7 | *arr [p] | test.cpp:67:10:67:19 | **mk_array_p [p] | provenance | |
|
||||
| test.cpp:76:20:76:29 | *call to mk_array_p [p] | test.cpp:79:9:79:11 | *arr [p] | provenance | |
|
||||
| test.cpp:76:20:76:29 | *call to mk_array_p [p] | test.cpp:83:9:83:11 | *arr [p] | provenance | |
|
||||
| test.cpp:79:9:79:11 | *arr [p] | test.cpp:79:14:79:14 | p | provenance | |
|
||||
@@ -48,7 +43,6 @@ edges
|
||||
| test.cpp:98:18:98:27 | test6_callee output argument [p] | test.cpp:98:18:98:27 | *call to mk_array_p [p] | provenance | |
|
||||
nodes
|
||||
| test.cpp:4:17:4:22 | call to malloc | semmle.label | call to malloc |
|
||||
| test.cpp:4:17:4:22 | call to malloc | semmle.label | call to malloc |
|
||||
| test.cpp:6:9:6:11 | arr | semmle.label | arr |
|
||||
| test.cpp:10:9:10:11 | arr | semmle.label | arr |
|
||||
| test.cpp:19:9:19:16 | *mk_array [p] | semmle.label | *mk_array [p] |
|
||||
@@ -56,8 +50,6 @@ nodes
|
||||
| test.cpp:21:5:21:24 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:21:13:21:18 | call to malloc | semmle.label | call to malloc |
|
||||
| test.cpp:22:5:22:7 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:24:12:24:14 | arr [p] | semmle.label | arr [p] |
|
||||
| test.cpp:28:19:28:26 | call to mk_array [p] | semmle.label | call to mk_array [p] |
|
||||
| test.cpp:28:19:28:26 | call to mk_array [p] | semmle.label | call to mk_array [p] |
|
||||
| test.cpp:31:9:31:11 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:31:13:31:13 | p | semmle.label | p |
|
||||
@@ -82,8 +74,6 @@ nodes
|
||||
| test.cpp:69:5:69:25 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:69:14:69:19 | call to malloc | semmle.label | call to malloc |
|
||||
| test.cpp:70:5:70:7 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:72:12:72:14 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:76:20:76:29 | *call to mk_array_p [p] | semmle.label | *call to mk_array_p [p] |
|
||||
| test.cpp:76:20:76:29 | *call to mk_array_p [p] | semmle.label | *call to mk_array_p [p] |
|
||||
| test.cpp:79:9:79:11 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:79:14:79:14 | p | semmle.label | p |
|
||||
|
||||
@@ -18,9 +18,8 @@ edges
|
||||
| test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | provenance | |
|
||||
| test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | provenance | |
|
||||
| test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | provenance | |
|
||||
| test.cpp:85:21:85:36 | buf | test.cpp:87:5:87:31 | access to array | provenance | |
|
||||
| test.cpp:85:21:85:36 | buf | test.cpp:88:5:88:27 | access to array | provenance | |
|
||||
| test.cpp:85:34:85:36 | buf | test.cpp:85:21:85:36 | buf | provenance | |
|
||||
| test.cpp:85:34:85:36 | buf | test.cpp:87:5:87:31 | access to array | provenance | |
|
||||
| test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | provenance | |
|
||||
| test.cpp:96:13:96:15 | arr | test.cpp:96:13:96:18 | access to array | provenance | |
|
||||
| test.cpp:111:17:111:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | |
|
||||
| test.cpp:111:17:111:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | |
|
||||
@@ -33,21 +32,17 @@ edges
|
||||
| test.cpp:119:17:119:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | |
|
||||
| test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | provenance | |
|
||||
| test.cpp:134:25:134:27 | arr | test.cpp:136:9:136:16 | ... += ... | provenance | |
|
||||
| test.cpp:136:9:136:16 | ... += ... | test.cpp:136:9:136:16 | ... += ... | provenance | |
|
||||
| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr | provenance | |
|
||||
| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr | provenance | |
|
||||
| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf | provenance | |
|
||||
| test.cpp:146:26:146:26 | *p | test.cpp:147:4:147:9 | -- ... | provenance | |
|
||||
| test.cpp:156:12:156:14 | buf | test.cpp:156:12:156:18 | ... + ... | provenance | |
|
||||
| test.cpp:156:12:156:18 | ... + ... | test.cpp:156:12:156:18 | ... + ... | provenance | |
|
||||
| test.cpp:156:12:156:18 | ... + ... | test.cpp:158:17:158:18 | *& ... | provenance | |
|
||||
| test.cpp:158:17:158:18 | *& ... | test.cpp:146:26:146:26 | *p | provenance | |
|
||||
| test.cpp:218:16:218:28 | buffer | test.cpp:220:5:220:11 | access to array | provenance | |
|
||||
| test.cpp:218:16:218:28 | buffer | test.cpp:221:5:221:11 | access to array | provenance | |
|
||||
| test.cpp:218:23:218:28 | buffer | test.cpp:218:16:218:28 | buffer | provenance | |
|
||||
| test.cpp:229:17:229:29 | array | test.cpp:231:5:231:10 | access to array | provenance | |
|
||||
| test.cpp:229:17:229:29 | array | test.cpp:232:5:232:10 | access to array | provenance | |
|
||||
| test.cpp:229:25:229:29 | array | test.cpp:229:17:229:29 | array | provenance | |
|
||||
| test.cpp:218:23:218:28 | buffer | test.cpp:220:5:220:11 | access to array | provenance | |
|
||||
| test.cpp:218:23:218:28 | buffer | test.cpp:221:5:221:11 | access to array | provenance | |
|
||||
| test.cpp:229:25:229:29 | array | test.cpp:231:5:231:10 | access to array | provenance | |
|
||||
| test.cpp:229:25:229:29 | array | test.cpp:232:5:232:10 | access to array | provenance | |
|
||||
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | |
|
||||
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | |
|
||||
| test.cpp:274:14:274:20 | buffer3 | test.cpp:245:30:245:30 | p | provenance | |
|
||||
@@ -66,16 +61,13 @@ edges
|
||||
| test.cpp:306:20:306:23 | arr1 | test.cpp:306:20:306:23 | arr1 | provenance | |
|
||||
| test.cpp:309:20:309:23 | arr2 | test.cpp:292:25:292:27 | arr | provenance | |
|
||||
| test.cpp:309:20:309:23 | arr2 | test.cpp:309:20:309:23 | arr2 | provenance | |
|
||||
| test.cpp:319:13:319:27 | ... = ... | test.cpp:325:24:325:26 | end | provenance | |
|
||||
| test.cpp:319:19:319:22 | temp | test.cpp:319:19:319:27 | ... + ... | provenance | |
|
||||
| test.cpp:319:19:319:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
||||
| test.cpp:319:19:319:27 | ... + ... | test.cpp:319:13:319:27 | ... = ... | provenance | |
|
||||
| test.cpp:322:13:322:27 | ... = ... | test.cpp:325:24:325:26 | end | provenance | |
|
||||
| test.cpp:319:19:319:27 | ... + ... | test.cpp:325:24:325:26 | end | provenance | |
|
||||
| test.cpp:322:19:322:22 | temp | test.cpp:322:19:322:27 | ... + ... | provenance | |
|
||||
| test.cpp:322:19:322:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
||||
| test.cpp:322:19:322:27 | ... + ... | test.cpp:322:13:322:27 | ... = ... | provenance | |
|
||||
| test.cpp:322:19:322:27 | ... + ... | test.cpp:325:24:325:26 | end | provenance | |
|
||||
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
||||
| test.cpp:324:23:324:32 | ... + ... | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
||||
| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 | provenance | |
|
||||
nodes
|
||||
| test.cpp:34:5:34:24 | access to array | semmle.label | access to array |
|
||||
@@ -111,7 +103,6 @@ nodes
|
||||
| test.cpp:77:32:77:34 | buf | semmle.label | buf |
|
||||
| test.cpp:79:27:79:34 | buf | semmle.label | buf |
|
||||
| test.cpp:79:32:79:34 | buf | semmle.label | buf |
|
||||
| test.cpp:85:21:85:36 | buf | semmle.label | buf |
|
||||
| test.cpp:85:34:85:36 | buf | semmle.label | buf |
|
||||
| test.cpp:87:5:87:31 | access to array | semmle.label | access to array |
|
||||
| test.cpp:88:5:88:27 | access to array | semmle.label | access to array |
|
||||
@@ -127,7 +118,6 @@ nodes
|
||||
| test.cpp:128:9:128:14 | access to array | semmle.label | access to array |
|
||||
| test.cpp:134:25:134:27 | arr | semmle.label | arr |
|
||||
| test.cpp:136:9:136:16 | ... += ... | semmle.label | ... += ... |
|
||||
| test.cpp:136:9:136:16 | ... += ... | semmle.label | ... += ... |
|
||||
| test.cpp:138:13:138:15 | arr | semmle.label | arr |
|
||||
| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
|
||||
| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
|
||||
@@ -135,13 +125,10 @@ nodes
|
||||
| test.cpp:147:4:147:9 | -- ... | semmle.label | -- ... |
|
||||
| test.cpp:156:12:156:14 | buf | semmle.label | buf |
|
||||
| test.cpp:156:12:156:18 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:156:12:156:18 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:158:17:158:18 | *& ... | semmle.label | *& ... |
|
||||
| test.cpp:218:16:218:28 | buffer | semmle.label | buffer |
|
||||
| test.cpp:218:23:218:28 | buffer | semmle.label | buffer |
|
||||
| test.cpp:220:5:220:11 | access to array | semmle.label | access to array |
|
||||
| test.cpp:221:5:221:11 | access to array | semmle.label | access to array |
|
||||
| test.cpp:229:17:229:29 | array | semmle.label | array |
|
||||
| test.cpp:229:25:229:29 | array | semmle.label | array |
|
||||
| test.cpp:231:5:231:10 | access to array | semmle.label | access to array |
|
||||
| test.cpp:232:5:232:10 | access to array | semmle.label | access to array |
|
||||
@@ -165,15 +152,12 @@ nodes
|
||||
| test.cpp:306:20:306:23 | arr1 | semmle.label | arr1 |
|
||||
| test.cpp:309:20:309:23 | arr2 | semmle.label | arr2 |
|
||||
| test.cpp:309:20:309:23 | arr2 | semmle.label | arr2 |
|
||||
| test.cpp:319:13:319:27 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:319:19:319:22 | temp | semmle.label | temp |
|
||||
| test.cpp:319:19:319:27 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:322:13:322:27 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:322:19:322:22 | temp | semmle.label | temp |
|
||||
| test.cpp:322:19:322:27 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:324:23:324:26 | temp | semmle.label | temp |
|
||||
| test.cpp:324:23:324:32 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:324:23:324:32 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:325:15:325:19 | temp2 | semmle.label | temp2 |
|
||||
| test.cpp:325:24:325:26 | end | semmle.label | end |
|
||||
| test.cpp:325:24:325:26 | end | semmle.label | end |
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
edges
|
||||
| test.cpp:45:18:45:23 | buffer | test.cpp:47:10:47:15 | buffer | provenance | |
|
||||
| test.cpp:47:10:47:15 | buffer | test.cpp:45:7:45:10 | *func | provenance | |
|
||||
| test.cpp:74:24:74:30 | medical | test.cpp:77:16:77:22 | medical | provenance | |
|
||||
| test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | *func | provenance | |
|
||||
| test.cpp:74:24:74:30 | medical | test.cpp:78:24:78:27 | temp | provenance | |
|
||||
| test.cpp:74:24:74:30 | medical | test.cpp:81:22:81:28 | medical | provenance | |
|
||||
| test.cpp:77:16:77:22 | medical | test.cpp:77:16:77:22 | medical | provenance | |
|
||||
| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp | provenance | |
|
||||
| test.cpp:77:16:77:22 | medical | test.cpp:81:22:81:28 | medical | provenance | |
|
||||
| test.cpp:81:17:81:20 | call to func | test.cpp:81:17:81:20 | call to func | provenance | |
|
||||
| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 | provenance | |
|
||||
| test.cpp:81:22:81:28 | medical | test.cpp:45:18:45:23 | buffer | provenance | |
|
||||
| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func | provenance | |
|
||||
@@ -15,15 +12,12 @@ edges
|
||||
nodes
|
||||
| test.cpp:45:7:45:10 | *func | semmle.label | *func |
|
||||
| test.cpp:45:18:45:23 | buffer | semmle.label | buffer |
|
||||
| test.cpp:47:10:47:15 | buffer | semmle.label | buffer |
|
||||
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
|
||||
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
|
||||
| test.cpp:77:16:77:22 | medical | semmle.label | medical |
|
||||
| test.cpp:77:16:77:22 | medical | semmle.label | medical |
|
||||
| test.cpp:78:24:78:27 | temp | semmle.label | temp |
|
||||
| test.cpp:81:17:81:20 | call to func | semmle.label | call to func |
|
||||
| test.cpp:81:17:81:20 | call to func | semmle.label | call to func |
|
||||
| test.cpp:81:22:81:28 | medical | semmle.label | medical |
|
||||
| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 |
|
||||
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
| test.cpp:680:30:680:30 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:680:17:680:17 | call to begin | call to begin |
|
||||
| test.cpp:680:30:680:30 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:680:17:680:17 | call to end | call to end |
|
||||
| test.cpp:683:31:683:32 | call to at | This object is destroyed before $@ is called. | test.cpp:683:17:683:17 | call to begin | call to begin |
|
||||
| test.cpp:683:31:683:32 | call to at | This object is destroyed before $@ is called. | test.cpp:683:17:683:17 | call to end | call to end |
|
||||
| test.cpp:689:46:689:58 | pointer to ~vector output argument | This object is destroyed before $@ is called. | test.cpp:689:60:689:62 | call to end | call to end |
|
||||
| test.cpp:702:27:702:27 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:703:19:703:23 | call to begin | call to begin |
|
||||
| test.cpp:702:27:702:27 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:703:36:703:38 | call to end | call to end |
|
||||
| test.cpp:727:23:727:23 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:750:17:750:17 | call to begin | call to begin |
|
||||
| test.cpp:727:23:727:23 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:750:17:750:17 | call to end | call to end |
|
||||
| test.cpp:735:23:735:23 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:759:17:759:17 | call to begin | call to begin |
|
||||
| test.cpp:735:23:735:23 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:759:17:759:17 | call to end | call to end |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user