Merge branch 'master' into python-objectapi-to-valueapi-signatureoverriddenmethod

This commit is contained in:
Rebecca Valentine
2020-04-01 07:42:10 -07:00
1026 changed files with 32428 additions and 29759 deletions

3
.gitignore vendored
View File

@@ -14,6 +14,9 @@
.vs/*
!.vs/VSWorkspaceSettings.json
# Byte-compiled python files
*.pyc
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/
.vscode/settings.json

View File

@@ -44,6 +44,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
* The `LocalScopeVariableReachability` library is deprecated in favor of
`StackVariableReachability`. The functionality is the same.
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
* The models library models `gets` and similar functions.
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
the following improvements:
* The library now models data flow through `strdup` and similar functions.

View File

@@ -21,6 +21,7 @@ The following changes in version 1.24 affect C# analysis in all applications.
| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | More results | Results are reported from parameters with a default value of `null`. |
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the value assigned is an (implicitly or explicitly) cast default-like value. For example, `var s = (string)null` and `string s = default`. |
| XPath injection (`cs/xml/xpath-injection`) | More results | The query now recognizes calls to methods on `System.Xml.XPath.XPathNavigator` objects. |
## Removal of old queries

View File

@@ -6,16 +6,18 @@
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
* Imports with the `.js` extension can now be resolved to a TypeScript file,
* Resolution of imports has improved, leading to more results from the security queries:
- Imports with the `.js` extension can now be resolved to a TypeScript file,
when the import refers to a file generated by TypeScript.
- Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
- Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
* The analysis of sanitizers has improved, leading to more accurate results from the security queries.
In particular:
- Sanitizer guards now act across function boundaries in more cases.
- Sanitizers can now better distinguish between a tainted value and an object _containing_ a tainted value.
* Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
* The call graph construction has been improved, leading to more results from the security queries:
* Call graph construction has been improved, leading to more results from the security queries:
- Calls can now be resolved to indirectly-defined class members in more cases.
- Calls through partial invocations such as `.bind` can now be resolved in more cases.
@@ -40,11 +42,14 @@
- [ncp](https://www.npmjs.com/package/ncp)
- [node-dir](https://www.npmjs.com/package/node-dir)
- [path-exists](https://www.npmjs.com/package/path-exists)
- [pg](https://www.npmjs.com/package/pg)
- [react](https://www.npmjs.com/package/react)
- [recursive-readdir](https://www.npmjs.com/package/recursive-readdir)
- [request](https://www.npmjs.com/package/request)
- [rimraf](https://www.npmjs.com/package/rimraf)
- [send](https://www.npmjs.com/package/send)
- [SockJS](https://www.npmjs.com/package/sockjs)
- [SockJS-client](https://www.npmjs.com/package/sockjs-client)
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
- [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
- [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)
@@ -80,8 +85,14 @@
| Use of password hash with insufficient computational effort (`js/insufficient-password-hash`) | Fewer false positive results | This query now recognizes additional cases that do not require secure hashing. |
| Useless regular-expression character escape (`js/useless-regexp-character-escape`) | Fewer false positive results | This query now distinguishes escapes in strings and regular expression literals. |
| Identical operands (`js/redundant-operation`) | Fewer results | This query now recognizes cases where the operands change a value using ++/-- expressions. |
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer results | This query now recognizes cases where a function uses the `Function.arguments` value to process a variable number of parameters. |
## Changes to libraries
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.
* An extensible model of the `EventEmitter` pattern has been implemented.
* Taint-tracking configurations now interact differently with the `data` flow label, which may affect queries
that combine taint-tracking and flow labels.
- Sources added by the 1-argument `isSource` predicate are associated with the `taint` label now, instead of the `data` label.
- Sanitizers now only block the `taint` label. As a result, sanitizers no longer block the flow of tainted values wrapped inside a property of an object.
To retain the old behavior, instead use a barrier, or block the `data` flow label using a labeled sanitizer.

View File

@@ -4,6 +4,8 @@ The following changes in version 1.24 affect Python analysis in all applications
## General improvements
Support for Django version 2.x and 3.x
## New queries
| **Query** | **Tags** | **Purpose** |
@@ -13,6 +15,7 @@ The following changes in version 1.24 affect Python analysis in all applications
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
| Uncontrolled command line (`py/command-line-injection`) | More results | We now model the `fabric` and `invoke` pacakges for command execution. |
### Web framework support

View File

@@ -39,6 +39,12 @@
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll"
],
"DataFlow Java/C++/C# Consistency checks": [
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll"
],
"C++ SubBasicBlocks": [
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"

140
config/sync-files.py Normal file
View File

@@ -0,0 +1,140 @@
#!/usr/bin/env python3
# Due to various technical limitations, we sometimes have files that need to be
# kept identical in the repository. This script loads a database of such
# files and can perform two functions: check whether they are still identical,
# and overwrite the others with a master copy if needed.
import hashlib
import shutil
import os
import sys
import json
import re
path = os.path
file_groups = {}
def add_prefix(prefix, relative):
result = path.join(prefix, relative)
if path.commonprefix((path.realpath(result), path.realpath(prefix))) != \
path.realpath(prefix):
raise Exception("Path {} is not below {}".format(
result, prefix))
return result
def load_if_exists(prefix, json_file_relative):
json_file_name = path.join(prefix, json_file_relative)
if path.isfile(json_file_name):
print("Loading file groups from", json_file_name)
with open(json_file_name, 'r', encoding='utf-8') as fp:
raw_groups = json.load(fp)
prefixed_groups = {
name: [
add_prefix(prefix, relative)
for relative in relatives
]
for name, relatives in raw_groups.items()
}
file_groups.update(prefixed_groups)
# Generates a list of C# test files that should be in sync
def csharp_test_files():
test_file_re = re.compile('.*(Bad|Good)[0-9]*\\.cs$')
csharp_doc_files = {
file:os.path.join(root, file)
for root, dirs, files in os.walk("csharp/ql/src")
for file in files
if test_file_re.match(file)
}
return {
"C# test '" + file + "'" : [os.path.join(root, file), csharp_doc_files[file]]
for root, dirs, files in os.walk("csharp/ql/test")
for file in files
if file in csharp_doc_files
}
def file_checksum(filename):
with open(filename, 'rb') as file_handle:
return hashlib.sha1(file_handle.read()).hexdigest()
def check_group(group_name, files, master_file_picker, emit_error):
checksums = {file_checksum(f) for f in files}
if len(checksums) == 1:
return
master_file = master_file_picker(files)
if master_file is None:
emit_error(__file__, 0,
"Files from group '"+ group_name +"' not in sync.")
emit_error(__file__, 0,
"Run this script with a file-name argument among the "
"following to overwrite the remaining files with the contents "
"of that file or run with the --latest switch to update each "
"group of files from the most recently modified file in the group.")
for filename in files:
emit_error(__file__, 0, " " + filename)
else:
print(" Syncing others from", master_file)
for filename in files:
if filename == master_file:
continue
print(" " + filename)
os.replace(filename, filename + '~')
shutil.copy(master_file, filename)
print(" Backups written with '~' appended to file names")
def chdir_repo_root():
root_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
os.chdir(root_path)
def choose_master_file(master_file, files):
if master_file in files:
return master_file
else:
return None
def choose_latest_file(files):
latest_time = None
latest_file = None
for filename in files:
file_time = os.path.getmtime(filename)
if (latest_time is None) or (latest_time < file_time):
latest_time = file_time
latest_file = filename
return latest_file
local_error_count = 0
def emit_local_error(path, line, error):
print('ERROR: ' + path + ':' + line + " - " + error)
global local_error_count
local_error_count += 1
# This function is invoked directly by a CI script, which passes a different error-handling
# callback.
def sync_identical_files(emit_error):
if len(sys.argv) == 1:
master_file_picker = lambda files: None
elif len(sys.argv) == 2:
if sys.argv[1] == "--latest":
master_file_picker = choose_latest_file
elif os.path.isfile(sys.argv[1]):
master_file_picker = lambda files: choose_master_file(sys.argv[1], files)
else:
raise Exception("File not found")
else:
raise Exception("Bad command line or file not found")
chdir_repo_root()
load_if_exists('.', 'config/identical-files.json')
file_groups.update(csharp_test_files())
for group_name, files in file_groups.items():
check_group(group_name, files, master_file_picker, emit_error)
def main():
sync_identical_files(emit_local_error)
if local_error_count > 0:
exit(1)
if __name__ == "__main__":
main()

View File

@@ -133,10 +133,16 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
*/
Type getUnspecifiedType() { result = getType().getUnspecifiedType() }
/** Gets the nth parameter of this function. */
/**
* Gets the nth parameter of this function. There is no result for the
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
*/
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
/** Gets a parameter of this function. */
/**
* Gets a parameter of this function. There is no result for the implicit
* `this` parameter, and there is no `...` varargs pseudo-parameter.
*/
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
/**

View File

@@ -381,10 +381,10 @@ class StaticStorageDurationVariable extends Variable {
}
/**
* Holds if the initializer for this variable is evaluated at compile time.
* Holds if the initializer for this variable is evaluated at runtime.
*/
predicate hasConstantInitialization() {
not runtimeExprInStaticInitializer(this.getInitializer().getExpr())
predicate hasDynamicInitialization() {
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
}
}
@@ -397,18 +397,28 @@ class StaticStorageDurationVariable extends Variable {
*/
private predicate runtimeExprInStaticInitializer(Expr e) {
inStaticInitializer(e) and
if e instanceof AggregateLiteral
if e instanceof AggregateLiteral // in sync with the cast in `inStaticInitializer`
then runtimeExprInStaticInitializer(e.getAChild())
else not e.getFullyConverted().isConstant()
}
/** Holds if `e` is part of the initializer of a `StaticStorageDurationVariable`. */
/**
* Holds if `e` is the initializer of a `StaticStorageDurationVariable`, either
* directly or below some top-level `AggregateLiteral`s.
*/
private predicate inStaticInitializer(Expr e) {
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
or
inStaticInitializer(e.getParent())
// The cast to `AggregateLiteral` ensures we only compute what'll later be
// needed by `runtimeExprInStaticInitializer`.
inStaticInitializer(e.getParent().(AggregateLiteral))
}
/**
* A C++ local variable declared as `static`.
*/
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
/**
* A C/C++ variable which has global scope or namespace scope. For example the
* variables `a` and `b` in the following code:

View File

@@ -441,9 +441,9 @@ private Node getControlOrderChildSparse(Node n, int i) {
* thus should not have control flow computed.
*/
private predicate skipInitializer(Initializer init) {
exists(LocalVariable local |
exists(StaticLocalVariable local |
init = local.getInitializer() and
local.(StaticStorageDurationVariable).hasConstantInitialization()
not local.hasDynamicInitialization()
)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -243,7 +243,7 @@ private module Cached {
* - Types are checked using the `compatibleTypes()` relation.
*/
cached
module Final {
private module Final {
/**
* Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps, not taking call contexts into account.

View File

@@ -0,0 +1,175 @@
/**
* Provides consistency queries for checking invariants in the language-specific
* data-flow classes and predicates.
*/
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
private import tainttracking1.TaintTrackingParameter::Private
private import tainttracking1.TaintTrackingParameter::Public
module Consistency {
private class RelevantNode extends Node {
RelevantNode() {
this instanceof ArgumentNode or
this instanceof ParameterNode or
this instanceof ReturnNode or
this = getAnOutNode(_, _) or
simpleLocalFlowStep(this, _) or
simpleLocalFlowStep(_, this) or
jumpStep(this, _) or
jumpStep(_, this) or
storeStep(this, _, _) or
storeStep(_, _, this) or
readStep(this, _, _) or
readStep(_, _, this) or
defaultAdditionalTaintStep(this, _) or
defaultAdditionalTaintStep(_, this)
}
}
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getEnclosingCallable()) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
}
query predicate uniqueTypeBound(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getTypeBound()) and
c != 1 and
msg = "Node should have one type bound but has " + c + "."
)
}
query predicate uniqueTypeRepr(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(getErasedRepr(n.getTypeBound())) and
c != 1 and
msg = "Node should have one type representation but has " + c + "."
)
}
query predicate uniqueNodeLocation(Node n, string msg) {
exists(int c |
c =
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
msg = "Node should have one location but has " + c + "."
)
}
query predicate missingLocation(string msg) {
exists(int c |
c =
strictcount(Node n |
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
) and
msg = "Nodes without location: " + c
)
}
query predicate uniqueNodeToString(Node n, string msg) {
exists(int c |
c = count(n.toString()) and
c != 1 and
msg = "Node should have one toString but has " + c + "."
)
}
query predicate missingToString(string msg) {
exists(int c |
c = strictcount(Node n | not exists(n.toString())) and
msg = "Nodes without toString: " + c
)
}
query predicate parameterCallable(ParameterNode p, string msg) {
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
msg = "Local flow step does not preserve enclosing callable."
}
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
not compatibleTypes(t, t) and
msg = "Type compatibility predicate is not reflexive."
}
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
c = n.getEnclosingCallable() and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
}
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
(
n = getAnOutNode(call, _) and
msg = "OutNode and call does not share enclosing callable."
or
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
n.getEnclosingCallable() != call.getEnclosingCallable()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
exists(int c |
c = count(n.getPreUpdateNode()) and
c != 1 and
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
)
}
query predicate uniquePostUpdate(Node n, string msg) {
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
msg = "Node has multiple PostUpdateNodes."
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate storeIsPostUpdate(Node n, string msg) {
storeStep(_, _, n) and
not n instanceof PostUpdateNode and
msg = "Store targets should be PostUpdateNodes."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
}

View File

@@ -293,3 +293,27 @@ class DataFlowCall extends Expr {
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }
/**
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
* modified or its modification cannot be observed, for example if it is a
* freshly created object that is not saved in a variable.
*
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) {
// Is the null pointer (or something that's not really a pointer)
exists(n.asExpr().getValue())
or
// Isn't a pointer or is a pointer to const
forall(DerivedType dt | dt = n.asExpr().getActualType() |
dt.getBaseType().isConst()
or
dt.getBaseType() instanceof RoutineType
)
or
// Isn't something we can track
n.asExpr() instanceof Call
// The above list of cases isn't exhaustive, but it narrows down the
// consistency alerts enough that most of them are interesting.
}

View File

@@ -1,3 +1,13 @@
/**
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
* exposed through the `Configuration` class. For some languages, this file
* exists in several identical copies, allowing queries to use multiple
* `Configuration` classes that depend on each other without introducing
* mutual recursion among those configurations.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private

View File

@@ -1,3 +1,13 @@
/**
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
* exposed through the `Configuration` class. For some languages, this file
* exists in several identical copies, allowing queries to use multiple
* `Configuration` classes that depend on each other without introducing
* mutual recursion among those configurations.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private

View File

@@ -8,6 +8,22 @@ abstract class BuiltInOperation extends Expr {
override string getCanonicalQLClass() { result = "BuiltInOperation" }
}
/**
* A C/C++ built-in operation that is used to support functions with variable numbers of arguments.
* This includes `va_start`, `va_end`, `va_copy`, and `va_arg`.
*/
class VarArgsExpr extends BuiltInOperation {
VarArgsExpr() {
this instanceof BuiltInVarArgsStart
or
this instanceof BuiltInVarArgsEnd
or
this instanceof BuiltInVarArg
or
this instanceof BuiltInVarArgCopy
}
}
/**
* A C/C++ `__builtin_va_start` built-in operation (used by some
* implementations of `va_start`).
@@ -20,6 +36,16 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
override string toString() { result = "__builtin_va_start" }
override string getCanonicalQLClass() { result = "BuiltInVarArgsStart" }
/**
* Gets the `va_list` argument.
*/
final Expr getVAList() { result = getChild(0) }
/**
* Gets the argument that specifies the last named parameter before the ellipsis.
*/
final VariableAccess getLastNamedParameter() { result = getChild(1) }
}
/**
@@ -35,6 +61,11 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
override string toString() { result = "__builtin_va_end" }
override string getCanonicalQLClass() { result = "BuiltInVarArgsEnd" }
/**
* Gets the `va_list` argument.
*/
final Expr getVAList() { result = getChild(0) }
}
/**
@@ -48,6 +79,11 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
override string toString() { result = "__builtin_va_arg" }
override string getCanonicalQLClass() { result = "BuiltInVarArg" }
/**
* Gets the `va_list` argument.
*/
final Expr getVAList() { result = getChild(0) }
}
/**
@@ -63,6 +99,16 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
override string toString() { result = "__builtin_va_copy" }
override string getCanonicalQLClass() { result = "BuiltInVarArgCopy" }
/**
* Gets the destination `va_list` argument.
*/
final Expr getDestinationVAList() { result = getChild(0) }
/**
* Gets the the source `va_list` argument.
*/
final Expr getSourceVAList() { result = getChild(1) }
}
/**

View File

@@ -196,7 +196,7 @@ class FunctionCall extends Call, @funbindexpr {
* constructor calls, this predicate instead gets the `Class` of the constructor
* being called.
*/
private Type getTargetType() { result = Call.super.getType().stripType() }
Type getTargetType() { result = Call.super.getType().stripType() }
/**
* Gets the expected return type of the function called by this call.

View File

@@ -60,7 +60,14 @@ private DataFlow::Node getNodeForSource(Expr source) {
(
result = DataFlow::exprNode(source)
or
result = DataFlow::definitionByReferenceNode(source)
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
result = DataFlow::definitionByReferenceNode(source) and
not argv(source.(VariableAccess).getTarget())
)
}
@@ -76,6 +83,8 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
@@ -96,6 +105,8 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
@@ -119,6 +130,8 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
@@ -153,6 +166,11 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
)
}
private predicate nodeIsBarrierIn(DataFlow::Node node) {
// don't use dataflow into taint sources, as this leads to duplicate results.
node = getNodeForSource(any(Expr e))
}
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Expressions computed from tainted data are also tainted
exists(CallInstruction call, int argIndex | call = i2 |
@@ -172,7 +190,7 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
i2.(UnaryInstruction).getUnary() = i1
or
i2.(ChiInstruction).getPartial() = i1 and
not isChiForAllAliasedMemory(i2)
not i2.isResultConflated()
or
exists(BinaryInstruction bin |
bin = i2 and
@@ -265,19 +283,6 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet
)
}
/**
* Holds if `chi` is on the chain of chi-instructions for all aliased memory.
* Taint should not pass through these instructions since they tend to mix up
* unrelated objects.
*/
private predicate isChiForAllAliasedMemory(Instruction instr) {
instr.(ChiInstruction).getTotal() instanceof AliasedDefinitionInstruction
or
isChiForAllAliasedMemory(instr.(ChiInstruction).getTotal())
or
isChiForAllAliasedMemory(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
}
private predicate modelTaintToReturnValue(Function f, int parameterIn) {
// Taint flow from parameter to return value
exists(FunctionInput modelIn, FunctionOutput modelOut |

View File

@@ -243,7 +243,7 @@ private module Cached {
* - Types are checked using the `compatibleTypes()` relation.
*/
cached
module Final {
private module Final {
/**
* Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps, not taking call contexts into account.

View File

@@ -0,0 +1,175 @@
/**
* Provides consistency queries for checking invariants in the language-specific
* data-flow classes and predicates.
*/
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
private import tainttracking1.TaintTrackingParameter::Private
private import tainttracking1.TaintTrackingParameter::Public
module Consistency {
private class RelevantNode extends Node {
RelevantNode() {
this instanceof ArgumentNode or
this instanceof ParameterNode or
this instanceof ReturnNode or
this = getAnOutNode(_, _) or
simpleLocalFlowStep(this, _) or
simpleLocalFlowStep(_, this) or
jumpStep(this, _) or
jumpStep(_, this) or
storeStep(this, _, _) or
storeStep(_, _, this) or
readStep(this, _, _) or
readStep(_, _, this) or
defaultAdditionalTaintStep(this, _) or
defaultAdditionalTaintStep(_, this)
}
}
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getEnclosingCallable()) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
}
query predicate uniqueTypeBound(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getTypeBound()) and
c != 1 and
msg = "Node should have one type bound but has " + c + "."
)
}
query predicate uniqueTypeRepr(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(getErasedRepr(n.getTypeBound())) and
c != 1 and
msg = "Node should have one type representation but has " + c + "."
)
}
query predicate uniqueNodeLocation(Node n, string msg) {
exists(int c |
c =
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
msg = "Node should have one location but has " + c + "."
)
}
query predicate missingLocation(string msg) {
exists(int c |
c =
strictcount(Node n |
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
) and
msg = "Nodes without location: " + c
)
}
query predicate uniqueNodeToString(Node n, string msg) {
exists(int c |
c = count(n.toString()) and
c != 1 and
msg = "Node should have one toString but has " + c + "."
)
}
query predicate missingToString(string msg) {
exists(int c |
c = strictcount(Node n | not exists(n.toString())) and
msg = "Nodes without toString: " + c
)
}
query predicate parameterCallable(ParameterNode p, string msg) {
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
msg = "Local flow step does not preserve enclosing callable."
}
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
not compatibleTypes(t, t) and
msg = "Type compatibility predicate is not reflexive."
}
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
c = n.getEnclosingCallable() and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
}
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
(
n = getAnOutNode(call, _) and
msg = "OutNode and call does not share enclosing callable."
or
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
n.getEnclosingCallable() != call.getEnclosingCallable()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
exists(int c |
c = count(n.getPreUpdateNode()) and
c != 1 and
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
)
}
query predicate uniquePostUpdate(Node n, string msg) {
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
msg = "Node has multiple PostUpdateNodes."
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate storeIsPostUpdate(Node n, string msg) {
storeStep(_, _, n) and
not n instanceof PostUpdateNode and
msg = "Store targets should be PostUpdateNodes."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
}

View File

@@ -202,3 +202,16 @@ class DataFlowCall extends CallInstruction {
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }
/**
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
* modified or its modification cannot be observed, for example if it is a
* freshly created object that is not saved in a variable.
*
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) {
// The rules for whether an IR argument gets a post-update node are too
// complex to model here.
any()
}

View File

@@ -1,3 +1,13 @@
/**
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
* exposed through the `Configuration` class. For some languages, this file
* exists in several identical copies, allowing queries to use multiple
* `Configuration` classes that depend on each other without introducing
* mutual recursion among those configurations.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private

View File

@@ -1,3 +1,13 @@
/**
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
* exposed through the `Configuration` class. For some languages, this file
* exists in several identical copies, allowing queries to use multiple
* `Configuration` classes that depend on each other without introducing
* mutual recursion among those configurations.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private

View File

@@ -16,6 +16,8 @@ class IRConfiguration extends TIRConfiguration {
* Holds if IR should be created for function `func`. By default, holds for all functions.
*/
predicate shouldCreateIRForFunction(Language::Function func) { any() }
predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() }
}
private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration()

View File

@@ -70,7 +70,7 @@ private newtype TOpcode =
TVarArgsStart() or
TVarArgsEnd() or
TVarArg() or
TVarArgCopy() or
TNextVarArg() or
TCallSideEffect() or
TCallReadSideEffect() or
TIndirectReadSideEffect() or
@@ -629,20 +629,20 @@ module Opcode {
final override string toString() { result = "BuiltIn" }
}
class VarArgsStart extends BuiltInOperationOpcode, TVarArgsStart {
class VarArgsStart extends UnaryOpcode, TVarArgsStart {
final override string toString() { result = "VarArgsStart" }
}
class VarArgsEnd extends BuiltInOperationOpcode, TVarArgsEnd {
class VarArgsEnd extends UnaryOpcode, TVarArgsEnd {
final override string toString() { result = "VarArgsEnd" }
}
class VarArg extends BuiltInOperationOpcode, TVarArg {
class VarArg extends UnaryOpcode, TVarArg {
final override string toString() { result = "VarArg" }
}
class VarArgCopy extends BuiltInOperationOpcode, TVarArgCopy {
final override string toString() { result = "VarArgCopy" }
class NextVarArg extends UnaryOpcode, TNextVarArg {
final override string toString() { result = "NextVarArg" }
}
class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode,

View File

@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
* by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock |
funcBlock.getEnclosingFunction() = getEnclosingFunction()

View File

@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
module InstructionSanity {
private import internal.InstructionImports as Imports
private import Imports::OperandTag
private import Imports::Overlap
private import internal.IRInternal
/**
@@ -272,4 +273,48 @@ module InstructionSanity {
func = switchInstr.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
}
/**
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
* memory.
*/
private predicate isOnAliasedDefinitionChain(Instruction instr) {
instr instanceof AliasedDefinitionInstruction
or
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
or
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
}
private predicate shouldBeConflated(Instruction instr) {
isOnAliasedDefinitionChain(instr)
or
instr instanceof UnmodeledDefinitionInstruction
or
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
query predicate notMarkedAsConflated(Instruction instr) {
shouldBeConflated(instr) and
not instr.isResultConflated()
}
query predicate wronglyMarkedAsConflated(Instruction instr) {
instr.isResultConflated() and
not shouldBeConflated(instr)
}
query predicate invalidOverlap(
MemoryOperand useOperand, string message, IRFunction func, string funcText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
overlap instanceof MayPartiallyOverlap and
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
func = useOperand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
)
}
}

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
}
/**
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -15,6 +15,9 @@ private import Imports::OperandTag
* `File` and line number. Used for assigning register names when printing IR.
*/
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
) and
exists(Language::Location location |
irFunc = result.getEnclosingIRFunction() and
location = result.getLocation() and
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
private predicate shouldGenerateDumpStrings() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
)
}
/**
* Gets a string describing the operation of this instruction. This includes
* the opcode and the immediate value, if any. For example:
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
* VariableAddress[x]
*/
final string getOperationString() {
shouldGenerateDumpStrings() and
if exists(getImmediateString())
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
else result = getOperationPrefix() + getOpcode().toString()
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
string getImmediateString() { none() }
private string getOperationPrefix() {
shouldGenerateDumpStrings() and
if this instanceof SideEffectInstruction then result = "^" else result = ""
}
private string getResultPrefix() {
shouldGenerateDumpStrings() and
if getResultIRType() instanceof IRVoidType
then result = "v"
else
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
* used by debugging and printing code only.
*/
int getDisplayIndexInBlock() {
shouldGenerateDumpStrings() and
exists(IRBlock block |
this = block.getInstruction(result)
or
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
}
private int getLineRank() {
shouldGenerateDumpStrings() and
this =
rank[result](Instruction instr |
instr =
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1`
*/
string getResultId() {
shouldGenerateDumpStrings() and
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
}
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1(int*)`
*/
final string getResultString() {
shouldGenerateDumpStrings() and
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
}
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
* Example: `func:r3_4, this:r3_5`
*/
string getOperandsString() {
shouldGenerateDumpStrings() and
result =
concat(Operand operand |
operand = getAnOperand()
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
Construction::hasModeledMemoryResult(this)
}
/**
* Holds if this is an instruction with a memory result that represents a
* conflation of more than one memory allocation.
*
* This happens in practice when dereferencing a pointer that cannot be
* tracked back to a single local allocation. Such memory is instead modeled
* as originating on the `AliasedDefinitionInstruction` at the entry of the
* function.
*/
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
/**
* Gets the successor of this instruction along the control flow edge
* specified by `kind`.

View File

@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
override string toString() { result = "SideEffect" }
}
/**

View File

@@ -18,19 +18,19 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
predicate shouldPrintFunction(Language::Function func) { any() }
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
/**
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldCreateIRForFunction(Language::Function func) {
override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
shouldPrintFunction(func)
}
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
}

View File

@@ -354,6 +354,7 @@ class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
final override predicate isMayAccess() { isMayAccess = true }
}
/** A virtual variable that groups all escaped memory within a function. */
class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
AliasedVirtualVariable() { not isMayAccess() }
}
@@ -429,10 +430,18 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
use instanceof EntireAllocationMemoryLocation and
result instanceof MustExactlyOverlap
or
// EntireAllocationMemoryLocation totally overlaps any location within the same virtual
// variable.
not use instanceof EntireAllocationMemoryLocation and
result instanceof MustTotallyOverlap
if def.getAllocation() = use.getAllocation()
then
// EntireAllocationMemoryLocation totally overlaps any location within
// the same allocation.
result instanceof MustTotallyOverlap
else (
// There is no overlap with a location that's known to belong to a
// different allocation, but all other locations may partially overlap.
not exists(use.getAllocation()) and
result instanceof MayPartiallyOverlap
)
)
or
exists(VariableMemoryLocation defVariableLocation |

View File

@@ -1,2 +1,3 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SSAConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration

View File

@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.Opcode as Opcode
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap

View File

@@ -65,6 +65,29 @@ private module Cached {
instruction instanceof ChiInstruction // Chis always have modeled results
}
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
or
// Chi instructions track virtual variables, and therefore a chi instruction is
// conflated if it's associated with the aliased virtual variable.
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
Alias::AliasedVirtualVariable
)
or
// Phi instructions track locations, and therefore a phi instruction is
// conflated if it's associated with a conflated location.
exists(Alias::MemoryLocation location |
instruction = Phi(_, location) and
not exists(location.getAllocation())
)
}
cached
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand |
@@ -84,14 +107,15 @@ private module Cached {
oldOperand instanceof OldIR::NonPhiMemoryOperand and
exists(
OldBlock useBlock, int useRank, Alias::MemoryLocation useLocation,
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset,
Alias::MemoryLocation actualDefLocation
|
useLocation = Alias::getOperandMemoryLocation(oldOperand) and
hasUseAtRank(useLocation, useBlock, useRank, oldInstruction) and
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank) and
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, _) and
overlap = Alias::getOverlap(defLocation, useLocation)
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
)
}

View File

@@ -10,6 +10,11 @@ newtype TIRVariable =
) {
Construction::hasTempVariable(func, ast, tag, type)
} or
TIRDynamicInitializationFlag(
Language::Function func, Language::Variable var, Language::LanguageType type
) {
Construction::hasDynamicInitializationFlag(func, var, type)
} or
TIRStringLiteral(
Language::Function func, Language::AST ast, Language::LanguageType type,
Language::StringLiteral literal

View File

@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
* by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock |
funcBlock.getEnclosingFunction() = getEnclosingFunction()

View File

@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
module InstructionSanity {
private import internal.InstructionImports as Imports
private import Imports::OperandTag
private import Imports::Overlap
private import internal.IRInternal
/**
@@ -272,4 +273,48 @@ module InstructionSanity {
func = switchInstr.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
}
/**
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
* memory.
*/
private predicate isOnAliasedDefinitionChain(Instruction instr) {
instr instanceof AliasedDefinitionInstruction
or
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
or
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
}
private predicate shouldBeConflated(Instruction instr) {
isOnAliasedDefinitionChain(instr)
or
instr instanceof UnmodeledDefinitionInstruction
or
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
query predicate notMarkedAsConflated(Instruction instr) {
shouldBeConflated(instr) and
not instr.isResultConflated()
}
query predicate wronglyMarkedAsConflated(Instruction instr) {
instr.isResultConflated() and
not shouldBeConflated(instr)
}
query predicate invalidOverlap(
MemoryOperand useOperand, string message, IRFunction func, string funcText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
overlap instanceof MayPartiallyOverlap and
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
func = useOperand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
)
}
}

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
}
/**
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -15,6 +15,9 @@ private import Imports::OperandTag
* `File` and line number. Used for assigning register names when printing IR.
*/
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
) and
exists(Language::Location location |
irFunc = result.getEnclosingIRFunction() and
location = result.getLocation() and
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
private predicate shouldGenerateDumpStrings() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
)
}
/**
* Gets a string describing the operation of this instruction. This includes
* the opcode and the immediate value, if any. For example:
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
* VariableAddress[x]
*/
final string getOperationString() {
shouldGenerateDumpStrings() and
if exists(getImmediateString())
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
else result = getOperationPrefix() + getOpcode().toString()
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
string getImmediateString() { none() }
private string getOperationPrefix() {
shouldGenerateDumpStrings() and
if this instanceof SideEffectInstruction then result = "^" else result = ""
}
private string getResultPrefix() {
shouldGenerateDumpStrings() and
if getResultIRType() instanceof IRVoidType
then result = "v"
else
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
* used by debugging and printing code only.
*/
int getDisplayIndexInBlock() {
shouldGenerateDumpStrings() and
exists(IRBlock block |
this = block.getInstruction(result)
or
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
}
private int getLineRank() {
shouldGenerateDumpStrings() and
this =
rank[result](Instruction instr |
instr =
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1`
*/
string getResultId() {
shouldGenerateDumpStrings() and
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
}
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1(int*)`
*/
final string getResultString() {
shouldGenerateDumpStrings() and
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
}
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
* Example: `func:r3_4, this:r3_5`
*/
string getOperandsString() {
shouldGenerateDumpStrings() and
result =
concat(Operand operand |
operand = getAnOperand()
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
Construction::hasModeledMemoryResult(this)
}
/**
* Holds if this is an instruction with a memory result that represents a
* conflation of more than one memory allocation.
*
* This happens in practice when dereferencing a pointer that cannot be
* tracked back to a single local allocation. Such memory is instead modeled
* as originating on the `AliasedDefinitionInstruction` at the entry of the
* function.
*/
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
/**
* Gets the successor of this instruction along the control flow edge
* specified by `kind`.

View File

@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
override string toString() { result = "SideEffect" }
}
/**

View File

@@ -18,19 +18,19 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
predicate shouldPrintFunction(Language::Function func) { any() }
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
/**
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldCreateIRForFunction(Language::Function func) {
override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
shouldPrintFunction(func)
}
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
}

View File

@@ -51,9 +51,25 @@ private module Cached {
getTypeForPRValue(literal.getType()) = type
}
cached
predicate hasDynamicInitializationFlag(Function func, StaticLocalVariable var, CppType type) {
var.getFunction() = func and
var.hasDynamicInitialization() and
type = getBoolType()
}
cached
predicate hasModeledMemoryResult(Instruction instruction) { none() }
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
}
cached
Expr getInstructionConvertedResultExpression(Instruction instruction) {
exists(TranslatedExpr translatedExpr |

View File

@@ -1,2 +1,3 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import IRConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration

View File

@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.Opcode as Opcode
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap

View File

@@ -8,6 +8,11 @@ newtype TInstructionTag =
InitializerStoreTag() or
InitializerIndirectAddressTag() or
InitializerIndirectStoreTag() or
DynamicInitializationFlagAddressTag() or
DynamicInitializationFlagLoadTag() or
DynamicInitializationConditionalBranchTag() or
DynamicInitializationFlagConstantTag() or
DynamicInitializationFlagStoreTag() or
ZeroPadStringConstantTag() or
ZeroPadStringElementIndexTag() or
ZeroPadStringElementAddressTag() or
@@ -59,6 +64,13 @@ newtype TInstructionTag =
InitializerElementAddressTag() or
InitializerElementDefaultValueTag() or
InitializerElementDefaultValueStoreTag() or
VarArgsStartEllipsisAddressTag() or
VarArgsStartTag() or
VarArgsVAListLoadTag() or
VarArgsArgAddressTag() or
VarArgsArgLoadTag() or
VarArgsMoveNextTag() or
VarArgsVAListStoreTag() or
AsmTag() or
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) }
@@ -183,7 +195,31 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = InitializerElementDefaultValueStoreTag() and result = "InitElemDefValStore"
or
tag = VarArgsStartEllipsisAddressTag() and result = "VarArgsStartEllipsisAddr"
or
tag = VarArgsStartTag() and result = "VarArgsStart"
or
tag = VarArgsVAListLoadTag() and result = "VarArgsVAListLoad"
or
tag = VarArgsArgAddressTag() and result = "VarArgsArgAddr"
or
tag = VarArgsArgLoadTag() and result = "VaArgsArgLoad"
or
tag = VarArgsMoveNextTag() and result = "VarArgsMoveNext"
or
tag = VarArgsVAListStoreTag() and result = "VarArgsVAListStore"
or
tag = AsmTag() and result = "Asm"
or
exists(int index | tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")")
or
tag = DynamicInitializationFlagAddressTag() and result = "DynInitFlagAddr"
or
tag = DynamicInitializationFlagLoadTag() and result = "DynInitFlagLoad"
or
tag = DynamicInitializationConditionalBranchTag() and result = "DynInitCondBranch"
or
tag = DynamicInitializationFlagConstantTag() and result = "DynInitFlagConst"
or
tag = DynamicInitializationFlagStoreTag() and result = "DynInitFlagStore"
}

View File

@@ -6,6 +6,7 @@ private import semmle.code.cpp.ir.internal.IRUtilities
private import InstructionTag
private import TranslatedElement
private import TranslatedExpr
private import TranslatedFunction
private import TranslatedInitialization
/**
@@ -66,17 +67,172 @@ abstract class TranslatedLocalVariableDeclaration extends TranslatedVariableInit
}
/**
* Represents the IR translation of a local variable declaration within a declaration statement.
* The IR translation of a local variable declaration within a declaration statement.
*/
class TranslatedVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
class TranslatedAutoVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
TranslatedDeclarationEntry {
LocalVariable var;
StackVariable var;
TranslatedVariableDeclarationEntry() { var = entry.getDeclaration() }
TranslatedAutoVariableDeclarationEntry() { var = entry.getDeclaration() }
override LocalVariable getVariable() { result = var }
}
/**
* The IR translation of the declaration of a static local variable.
* This element generates the logic that determines whether or not the variable has already been
* initialized, and if not, invokes the initializer and sets the dynamic initialization flag for the
* variable. The actual initialization code is handled in
* `TranslatedStaticLocalVariableInitialization`, which is a child of this element.
*
* The generated code to do the initialization only once is:
* ```
* Block 1
* r1225_1(glval<bool>) = VariableAddress[c#init] :
* r1225_2(bool) = Load : &:r1225_1, ~mu1222_4
* v1225_3(void) = ConditionalBranch : r1225_2
* False -> Block 2
* True -> Block 3
*
* Block 2
* r1225_4(glval<int>) = VariableAddress[c] :
* <actual initialization of `c`>
* r1225_8(bool) = Constant[1] :
* mu1225_9(bool) = Store : &:r1225_1, r1225_8
* Goto -> Block 3
*
* Block 3
* ```
*
* Note that the flag variable, `c#init`, is assumed to be zero-initialized at program startup, just
* like any other variable with static storage duration.
*/
class TranslatedStaticLocalVariableDeclarationEntry extends TranslatedDeclarationEntry {
StaticLocalVariable var;
TranslatedStaticLocalVariableDeclarationEntry() { var = entry.getDeclaration() }
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
tag = DynamicInitializationFlagAddressTag() and
opcode instanceof Opcode::VariableAddress and
type = getBoolGLValueType()
or
tag = DynamicInitializationFlagLoadTag() and
opcode instanceof Opcode::Load and
type = getBoolType()
or
tag = DynamicInitializationConditionalBranchTag() and
opcode instanceof Opcode::ConditionalBranch and
type = getVoidType()
or
tag = DynamicInitializationFlagConstantTag() and
opcode instanceof Opcode::Constant and
type = getBoolType()
or
tag = DynamicInitializationFlagStoreTag() and
opcode instanceof Opcode::Store and
type = getBoolType()
}
final override Instruction getFirstInstruction() {
result = getInstruction(DynamicInitializationFlagAddressTag())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = DynamicInitializationFlagAddressTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationFlagLoadTag())
or
tag = DynamicInitializationFlagLoadTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationConditionalBranchTag())
or
tag = DynamicInitializationConditionalBranchTag() and
(
kind instanceof TrueEdge and
result = getParent().getChildSuccessor(this)
or
kind instanceof FalseEdge and
result = getInitialization().getFirstInstruction()
)
or
tag = DynamicInitializationFlagConstantTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationFlagStoreTag())
or
tag = DynamicInitializationFlagStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and
result = getInstruction(DynamicInitializationFlagConstantTag())
}
final override IRDynamicInitializationFlag getInstructionVariable(InstructionTag tag) {
tag = DynamicInitializationFlagAddressTag() and
result.getVariable() = var
}
final override string getInstructionConstantValue(InstructionTag tag) {
tag = DynamicInitializationFlagConstantTag() and result = "1"
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = DynamicInitializationFlagLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(DynamicInitializationFlagAddressTag())
or
operandTag instanceof LoadOperandTag and
result = getTranslatedFunction(var.getFunction()).getUnmodeledDefinitionInstruction()
)
or
tag = DynamicInitializationConditionalBranchTag() and
operandTag instanceof ConditionOperandTag and
result = getInstruction(DynamicInitializationFlagLoadTag())
or
tag = DynamicInitializationFlagStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(DynamicInitializationFlagAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(DynamicInitializationFlagConstantTag())
)
}
private TranslatedStaticLocalVariableInitialization getInitialization() {
result.getVariable() = var
}
}
/**
* The initialization of a static local variable. This element will only exist for a static variable
* with a dynamic initializer.
*/
class TranslatedStaticLocalVariableInitialization extends TranslatedElement,
TranslatedLocalVariableDeclaration, TTranslatedStaticLocalVariableInitialization {
VariableDeclarationEntry entry;
StaticLocalVariable var;
TranslatedStaticLocalVariableInitialization() {
this = TTranslatedStaticLocalVariableInitialization(entry) and
var = entry.getDeclaration()
}
final override string toString() { result = "init: " + entry.toString() }
final override Locatable getAST() { result = entry }
final override LocalVariable getVariable() { result = var }
final override Function getFunction() { result = var.getFunction() }
}
/**
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
* `var`.

View File

@@ -54,7 +54,7 @@ private predicate ignoreExprAndDescendants(Expr expr) {
// Otherwise the initializer does not run in function scope.
exists(Initializer init, StaticStorageDurationVariable var |
init = var.getInitializer() and
var.hasConstantInitialization() and
not var.hasDynamicInitialization() and
expr = init.getExpr().getFullyConverted()
)
or
@@ -83,6 +83,10 @@ private predicate ignoreExprAndDescendants(Expr expr) {
exists(DeleteExpr deleteExpr | deleteExpr.getAllocatorCall() = expr)
or
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getAllocatorCall() = expr)
or
exists(BuiltInVarArgsStart vaStartExpr |
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
)
}
/**
@@ -256,6 +260,26 @@ predicate hasTranslatedLoad(Expr expr) {
not ignoreLoad(expr)
}
/**
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
* necessary for automatic local variables, or for static local variables with dynamic
* initialization.
*/
private predicate translateDeclarationEntry(DeclarationEntry entry) {
exists(DeclStmt declStmt, LocalVariable var |
translateStmt(declStmt) and
declStmt.getADeclarationEntry() = entry and
// Only declarations of local variables need to be translated to IR.
var = entry.getDeclaration() and
(
not var.isStatic()
or
// Ignore static variables unless they have a dynamic initializer.
var.(StaticLocalVariable).hasDynamicInitialization()
)
)
}
newtype TTranslatedElement =
// An expression that is not being consumed as a condition
TTranslatedValueExpr(Expr expr) {
@@ -382,6 +406,7 @@ newtype TTranslatedElement =
translateFunction(func)
)
} or
TTranslatedEllipsisParameter(Function func) { translateFunction(func) and func.isVarargs() } or
TTranslatedReadEffects(Function func) { translateFunction(func) } or
// The read side effects in a function's return block
TTranslatedReadEffect(Parameter param) {
@@ -393,13 +418,12 @@ newtype TTranslatedElement =
)
} or
// A local declaration
TTranslatedDeclarationEntry(DeclarationEntry entry) {
exists(DeclStmt declStmt |
translateStmt(declStmt) and
declStmt.getADeclarationEntry() = entry and
// Only declarations of local variables need to be translated to IR.
entry.getDeclaration() instanceof LocalVariable
)
TTranslatedDeclarationEntry(DeclarationEntry entry) { translateDeclarationEntry(entry) } or
// The dynamic initialization of a static local variable. This is a separate object from the
// declaration entry.
TTranslatedStaticLocalVariableInitialization(DeclarationEntry entry) {
translateDeclarationEntry(entry) and
entry.getDeclaration() instanceof StaticLocalVariable
} or
// A compiler-generated variable to implement a range-based for loop. These don't have a
// `DeclarationEntry` in the database, so we have to go by the `Variable` itself.

View File

@@ -2020,7 +2020,12 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
override BuiltInOperation expr;
TranslatedBuiltInOperation() {
not expr instanceof BuiltInOperationBuiltInAddressOf // Handled specially
// The following expressions are handled specially.
not expr instanceof BuiltInOperationBuiltInAddressOf and
not expr instanceof BuiltInVarArgsStart and
not expr instanceof BuiltInVarArg and
not expr instanceof BuiltInVarArgsEnd and
not expr instanceof BuiltInVarArgCopy
}
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
@@ -2075,39 +2080,318 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
}
/**
* The IR translation of a `BuiltInVarArgsStart` expression.
* Holds if the expression `expr` is one of the `va_list` operands to a `va_*` macro.
*/
class TranslatedVarArgsStart extends TranslatedBuiltInOperation {
override BuiltInVarArgsStart expr;
final override Opcode getOpcode() { result instanceof Opcode::VarArgsStart }
private predicate isVAListExpr(Expr expr) {
exists(VarArgsExpr parent, Expr originalExpr |
(
originalExpr = parent.(BuiltInVarArgsStart).getVAList()
or
originalExpr = parent.(BuiltInVarArgsEnd).getVAList()
or
originalExpr = parent.(BuiltInVarArg).getVAList()
or
originalExpr = parent.(BuiltInVarArgCopy).getSourceVAList()
or
originalExpr = parent.(BuiltInVarArgCopy).getDestinationVAList()
) and
expr = originalExpr.getFullyConverted()
)
}
/**
* The IR translation of a `BuiltInVarArgsEnd` expression.
* Gets the type of the `va_list` being accessed by `expr`, where `expr` is a `va_list` operand of a
* `va_*` macro.
*
* In the Unix ABI, `va_list` is declared as `typedef struct __va_list_tag va_list[1];`. When used
* as the type of a local variable, this gets an implicit array-to-pointer conversion, so that the
* actual argument to the `va_*` macro is a prvalue of type `__va_list_tag*`. When used as the type
* of a function parameter, the parameter's type decays to `__va_list_tag*`, so that the argument
* to the `va_*` macro is still a prvalue of type `__va_list_tag*`, with no implicit conversion
* necessary. In either case, we treat `__va_list_tag` as the representative type of the `va_list`.
*
* In the Windows ABI, `va_list` is declared as a pointer type (usually `char*`). Whether used as
* the type of a local variable or of a parameter, this means that the argument to the `va_*` macro
* is always an _lvalue_ of type `char*`. We treat `char*` as the representative type of the
* `va_list`.
*/
class TranslatedVarArgsEnd extends TranslatedBuiltInOperation {
override BuiltInVarArgsEnd expr;
private Type getVAListType(Expr expr) {
isVAListExpr(expr) and
if expr.isPRValueCategory()
then
// In the Unix ABI, this will be a prvalue of type `__va_list_tag*`. We want the `__va_list_tag`
// type.
result = expr.getType().getUnderlyingType().(PointerType).getBaseType()
else
// In the Windows ABI, this will be an lvalue of some pointer type. We want that pointer type.
result = expr.getType()
}
final override Opcode getOpcode() { result instanceof Opcode::VarArgsEnd }
/**
* The IR translation of a `BuiltInVarArgsStart` expression.
*/
class TranslatedVarArgsStart extends TranslatedNonConstantExpr {
override BuiltInVarArgsStart expr;
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = VarArgsStartEllipsisAddressTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getEllipsisVariableGLValueType()
or
tag = VarArgsStartTag() and
opcode instanceof Opcode::VarArgsStart and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
or
tag = VarArgsVAListStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
}
final override Instruction getFirstInstruction() {
result = getInstruction(VarArgsStartEllipsisAddressTag())
}
final override Instruction getResult() { none() }
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
private TranslatedExpr getVAList() {
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = VarArgsStartEllipsisAddressTag() and
kind instanceof GotoEdge and
result = getInstruction(VarArgsStartTag())
or
tag = VarArgsStartTag() and
kind instanceof GotoEdge and
result = getVAList().getFirstInstruction()
or
tag = VarArgsVAListStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getVAList() and
result = getInstruction(VarArgsVAListStoreTag())
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
tag = VarArgsStartEllipsisAddressTag() and
result = getEnclosingFunction().getEllipsisVariable()
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsStartTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(VarArgsStartEllipsisAddressTag())
or
tag = VarArgsVAListStoreTag() and
(
operandTag instanceof AddressOperandTag and result = getVAList().getResult()
or
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsStartTag())
)
}
}
/**
* The IR translation of a `BuiltInVarArg` expression.
*/
class TranslatedVarArg extends TranslatedBuiltInOperation {
class TranslatedVarArg extends TranslatedNonConstantExpr {
override BuiltInVarArg expr;
final override Opcode getOpcode() { result instanceof Opcode::VarArg }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = VarArgsVAListLoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
or
tag = VarArgsArgAddressTag() and
opcode instanceof Opcode::VarArg and
resultType = getResultType()
or
tag = VarArgsMoveNextTag() and
opcode instanceof Opcode::NextVarArg and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
or
tag = VarArgsVAListStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
}
final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() }
final override Instruction getResult() { result = getInstruction(VarArgsArgAddressTag()) }
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
private TranslatedExpr getVAList() {
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = VarArgsVAListLoadTag() and
kind instanceof GotoEdge and
result = getInstruction(VarArgsArgAddressTag())
or
tag = VarArgsArgAddressTag() and
kind instanceof GotoEdge and
result = getInstruction(VarArgsMoveNextTag())
or
tag = VarArgsMoveNextTag() and
kind instanceof GotoEdge and
result = getInstruction(VarArgsVAListStoreTag())
or
tag = VarArgsVAListStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getVAList() and
result = getInstruction(VarArgsVAListLoadTag())
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsVAListLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getVAList().getResult()
or
operandTag instanceof LoadOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = VarArgsArgAddressTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(VarArgsVAListLoadTag())
or
tag = VarArgsMoveNextTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(VarArgsVAListLoadTag())
or
tag = VarArgsVAListStoreTag() and
(
operandTag instanceof AddressOperandTag and result = getVAList().getResult()
or
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsMoveNextTag())
)
}
}
/**
* The IR translation of a `BuiltInVarArgsEnd` expression.
*/
class TranslatedVarArgsEnd extends TranslatedNonConstantExpr {
override BuiltInVarArgsEnd expr;
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::VarArgsEnd and
resultType = getVoidType()
}
final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() }
final override Instruction getResult() { none() }
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
private TranslatedExpr getVAList() {
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getVAList() and
result = getInstruction(OnlyInstructionTag())
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getVAList().getResult()
}
}
/**
* The IR translation of a `BuiltInVarArgCopy` expression.
*/
class TranslatedVarArgCopy extends TranslatedBuiltInOperation {
class TranslatedVarArgCopy extends TranslatedNonConstantExpr {
override BuiltInVarArgCopy expr;
final override Opcode getOpcode() { result instanceof Opcode::VarArgCopy }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = VarArgsVAListLoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(getVAListType(expr.getSourceVAList().getFullyConverted()))
or
tag = VarArgsVAListStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(getVAListType(expr.getDestinationVAList().getFullyConverted()))
}
final override Instruction getFirstInstruction() {
result = getSourceVAList().getFirstInstruction()
}
final override Instruction getResult() { result = getInstruction(VarArgsVAListStoreTag()) }
final override TranslatedElement getChild(int id) {
id = 0 and result = getDestinationVAList()
or
id = 1 and result = getSourceVAList()
}
private TranslatedExpr getDestinationVAList() {
result = getTranslatedExpr(expr.getDestinationVAList().getFullyConverted())
}
private TranslatedExpr getSourceVAList() {
result = getTranslatedExpr(expr.getSourceVAList().getFullyConverted())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = VarArgsVAListLoadTag() and
kind instanceof GotoEdge and
result = getDestinationVAList().getFirstInstruction()
or
tag = VarArgsVAListStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getSourceVAList() and
result = getInstruction(VarArgsVAListLoadTag())
or
child = getDestinationVAList() and
result = getInstruction(VarArgsVAListStoreTag())
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsVAListLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getSourceVAList().getResult()
or
operandTag instanceof LoadOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = VarArgsVAListStoreTag() and
(
operandTag instanceof AddressOperandTag and result = getDestinationVAList().getResult()
or
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsVAListLoadTag())
)
}
}
/**

View File

@@ -10,12 +10,45 @@ private import TranslatedElement
private import TranslatedExpr
private import TranslatedInitialization
private import TranslatedStmt
private import VarArgs
/**
* Gets the `TranslatedFunction` that represents function `func`.
*/
TranslatedFunction getTranslatedFunction(Function func) { result.getAST() = func }
/**
* Gets the size, in bytes, of the variable used to represent the `...` parameter in a varargs
* function. This is determined by finding the total size of all of the arguments passed to the
* `...` in each call in the program, and choosing the maximum of those, with a minimum of 8 bytes.
*/
private int getEllipsisVariableByteSize() {
result =
max(int variableSize |
variableSize =
max(Call call, int callSize |
callSize =
sum(int argIndex |
isEllipsisArgumentIndex(call, argIndex)
|
call.getArgument(argIndex).getType().getSize()
)
|
callSize
)
or
variableSize = 8
|
variableSize
)
}
CppType getEllipsisVariablePRValueType() {
result = getUnknownOpaqueType(getEllipsisVariableByteSize())
}
CppType getEllipsisVariableGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
/**
* Represents the IR translation of a function. This is the root elements for
* all other elements associated with this function.
@@ -60,6 +93,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
final private TranslatedParameter getParameter(int index) {
result = getTranslatedParameter(func.getParameter(index))
or
index = getEllipsisParameterIndexForFunction(func) and
result = getTranslatedEllipsisParameter(func)
}
final override Instruction getFirstInstruction() { result = getInstruction(EnterFunctionTag()) }
@@ -113,7 +149,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
final override Instruction getChildSuccessor(TranslatedElement child) {
exists(int paramIndex |
child = getParameter(paramIndex) and
if exists(func.getParameter(paramIndex + 1))
if
exists(func.getParameter(paramIndex + 1)) or
getEllipsisParameterIndexForFunction(func) = paramIndex + 1
then result = getParameter(paramIndex + 1).getFirstInstruction()
else result = getConstructorInitList().getFirstInstruction()
)
@@ -237,10 +275,18 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getReturnVariable()
}
final override predicate needsUnknownOpaqueType(int byteSize) {
byteSize = getEllipsisVariableByteSize()
}
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = ReturnValueTempVar() and
hasReturnValue() and
type = getTypeForPRValue(getReturnType())
or
tag = EllipsisTempVar() and
func.isVarargs() and
type = getEllipsisVariablePRValueType()
}
/**
@@ -258,6 +304,11 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getIRTempVariable(func, ReturnValueTempVar())
}
/**
* Get the variable that represents the `...` parameter, if any.
*/
final IREllipsisVariable getEllipsisVariable() { result.getEnclosingFunction() = func }
/**
* Holds if the function has a non-`void` return type.
*/
@@ -316,34 +367,29 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
}
/**
* Gets the `TranslatedParameter` that represents parameter `param`.
* Gets the `TranslatedPositionalParameter` that represents parameter `param`.
*/
TranslatedParameter getTranslatedParameter(Parameter param) { result.getAST() = param }
TranslatedPositionalParameter getTranslatedParameter(Parameter param) { result.getAST() = param }
/**
* Represents the IR translation of a function parameter, including the
* initialization of that parameter with the incoming argument.
* Gets the `TranslatedEllipsisParameter` for function `func`, if one exists.
*/
class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
Parameter param;
TranslatedEllipsisParameter getTranslatedEllipsisParameter(Function func) {
result.getFunction() = func
}
TranslatedParameter() { this = TTranslatedParameter(param) }
final override string toString() { result = param.toString() }
final override Locatable getAST() { result = param }
final override Function getFunction() {
result = param.getFunction() or
result = param.getCatchBlock().getEnclosingFunction()
}
/**
* The IR translation of a parameter to a function. This can be either a user-declared parameter
* (`TranslatedPositionParameter`) or the synthesized parameter used to represent a `...` in a
* varargs function (`TranslatedEllipsisParameter`).
*/
abstract class TranslatedParameter extends TranslatedElement {
final override TranslatedElement getChild(int id) { none() }
final override Instruction getFirstInstruction() {
result = getInstruction(InitializerVariableAddressTag())
}
final override TranslatedElement getChild(int id) { none() }
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
@@ -368,16 +414,16 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getTypeForGLValue(getVariableType(param))
resultType = getGLValueType()
or
tag = InitializerStoreTag() and
opcode instanceof Opcode::InitializeParameter and
resultType = getTypeForPRValue(getVariableType(param))
resultType = getPRValueType()
or
hasIndirection() and
tag = InitializerIndirectAddressTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(getVariableType(param))
resultType = getPRValueType()
or
hasIndirection() and
tag = InitializerIndirectStoreTag() and
@@ -391,7 +437,7 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
tag = InitializerVariableAddressTag() or
tag = InitializerIndirectStoreTag()
) and
result = getIRUserVariable(getFunction(), param)
result = getIRVariable()
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -416,13 +462,74 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
result = getInstruction(InitializerIndirectAddressTag())
}
predicate hasIndirection() {
abstract predicate hasIndirection();
abstract CppType getGLValueType();
abstract CppType getPRValueType();
abstract IRAutomaticVariable getIRVariable();
}
/**
* Represents the IR translation of a function parameter, including the
* initialization of that parameter with the incoming argument.
*/
class TranslatedPositionalParameter extends TranslatedParameter, TTranslatedParameter {
Parameter param;
TranslatedPositionalParameter() { this = TTranslatedParameter(param) }
final override string toString() { result = param.toString() }
final override Locatable getAST() { result = param }
final override Function getFunction() {
result = param.getFunction() or
result = param.getCatchBlock().getEnclosingFunction()
}
final override predicate hasIndirection() {
exists(Type t | t = param.getUnspecifiedType() |
t instanceof ArrayType or
t instanceof PointerType or
t instanceof ReferenceType
)
}
final override CppType getGLValueType() { result = getTypeForGLValue(getVariableType(param)) }
final override CppType getPRValueType() { result = getTypeForPRValue(getVariableType(param)) }
final override IRAutomaticUserVariable getIRVariable() {
result = getIRUserVariable(getFunction(), param)
}
}
/**
* The IR translation of the synthesized parameter used to represent the `...` in a varargs
* function.
*/
class TranslatedEllipsisParameter extends TranslatedParameter, TTranslatedEllipsisParameter {
Function func;
TranslatedEllipsisParameter() { this = TTranslatedEllipsisParameter(func) }
final override string toString() { result = "..." }
final override Locatable getAST() { result = func }
final override Function getFunction() { result = func }
final override predicate hasIndirection() { any() }
final override CppType getGLValueType() { result = getEllipsisVariableGLValueType() }
final override CppType getPRValueType() { result = getEllipsisVariablePRValueType() }
final override IREllipsisVariable getIRVariable() {
result = getTranslatedFunction(func).getEllipsisVariable()
}
}
private TranslatedConstructorInitList getTranslatedConstructorInitList(Function func) {

View File

@@ -115,10 +115,14 @@ abstract class TranslatedVariableInitialization extends TranslatedElement, Initi
* evaluating the initializer.
*/
final predicate hasUninitializedInstruction() {
not exists(getInitialization()) or
getInitialization() instanceof TranslatedListInitialization or
getInitialization() instanceof TranslatedConstructorInitialization or
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
(
not exists(getInitialization()) or
getInitialization() instanceof TranslatedListInitialization or
getInitialization() instanceof TranslatedConstructorInitialization or
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
) and
// Variables with static or thread-local storage duration are zero-initialized at program startup.
getIRVariable() instanceof IRAutomaticVariable
}
}

View File

@@ -0,0 +1,62 @@
/**
* Utilities for determining which parameters and arguments correspond to the `...` parameter for
* varargs functions.
*/
private import cpp
/**
* Gets the index of the `...` parameter, if any. If present, the value will always be equal to
* `func.getNumberOfParameters()`.
*/
int getEllipsisParameterIndexForFunction(Function func) {
func.isVarargs() and result = func.getNumberOfParameters()
}
/**
* Gets the index of the `...` parameter, if any.
*/
int getEllipsisParameterIndexForRoutineType(RoutineType type) {
// Since the extractor doesn't record this information directly, we look for routine types whose
// last parameter type is `UnknownType`.
type.getParameterType(result) instanceof UnknownType and
result = strictcount(type.getAParameterType()) - 1
}
/**
* Gets the index of the `...` parameter, if any. This will be one greater than the index of the
* last declared positional parameter.
*/
int getEllipsisParameterIndex(Call call) {
exists(FunctionCall funcCall |
funcCall = call and
if funcCall.getTargetType() instanceof RoutineType
then result = getEllipsisParameterIndexForRoutineType(funcCall.getTargetType())
else result = getEllipsisParameterIndexForFunction(funcCall.getTarget())
)
or
exists(ExprCall exprCall |
exprCall = call and
result = getEllipsisParameterIndexForRoutineType(exprCall.getExpr().getType().stripType())
)
}
/**
* Gets the index of the parameter that will be initialized with the value of the argument
* specified by `argIndex`. For ordinary positional parameters, the argument and parameter indices
* will be equal. For a call to a varargs function, all arguments passed to the `...` will be
* mapped to the index returned by `getEllipsisParameterIndex()`.
*/
int getParameterIndexForArgument(Call call, int argIndex) {
exists(call.getArgument(argIndex)) and
if argIndex >= getEllipsisParameterIndex(call)
then result = getEllipsisParameterIndex(call)
else result = argIndex
}
/**
* Holds if the argument specified by `index` is an argument to the `...` of a varargs function.
*/
predicate isEllipsisArgumentIndex(Call call, int index) {
exists(call.getArgument(index)) and index >= getEllipsisParameterIndex(call)
}

View File

@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
* by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock |
funcBlock.getEnclosingFunction() = getEnclosingFunction()

View File

@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
module InstructionSanity {
private import internal.InstructionImports as Imports
private import Imports::OperandTag
private import Imports::Overlap
private import internal.IRInternal
/**
@@ -272,4 +273,48 @@ module InstructionSanity {
func = switchInstr.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
}
/**
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
* memory.
*/
private predicate isOnAliasedDefinitionChain(Instruction instr) {
instr instanceof AliasedDefinitionInstruction
or
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
or
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
}
private predicate shouldBeConflated(Instruction instr) {
isOnAliasedDefinitionChain(instr)
or
instr instanceof UnmodeledDefinitionInstruction
or
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
query predicate notMarkedAsConflated(Instruction instr) {
shouldBeConflated(instr) and
not instr.isResultConflated()
}
query predicate wronglyMarkedAsConflated(Instruction instr) {
instr.isResultConflated() and
not shouldBeConflated(instr)
}
query predicate invalidOverlap(
MemoryOperand useOperand, string message, IRFunction func, string funcText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
overlap instanceof MayPartiallyOverlap and
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
func = useOperand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
)
}
}

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
}
/**
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -15,6 +15,9 @@ private import Imports::OperandTag
* `File` and line number. Used for assigning register names when printing IR.
*/
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
) and
exists(Language::Location location |
irFunc = result.getEnclosingIRFunction() and
location = result.getLocation() and
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
private predicate shouldGenerateDumpStrings() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
)
}
/**
* Gets a string describing the operation of this instruction. This includes
* the opcode and the immediate value, if any. For example:
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
* VariableAddress[x]
*/
final string getOperationString() {
shouldGenerateDumpStrings() and
if exists(getImmediateString())
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
else result = getOperationPrefix() + getOpcode().toString()
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
string getImmediateString() { none() }
private string getOperationPrefix() {
shouldGenerateDumpStrings() and
if this instanceof SideEffectInstruction then result = "^" else result = ""
}
private string getResultPrefix() {
shouldGenerateDumpStrings() and
if getResultIRType() instanceof IRVoidType
then result = "v"
else
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
* used by debugging and printing code only.
*/
int getDisplayIndexInBlock() {
shouldGenerateDumpStrings() and
exists(IRBlock block |
this = block.getInstruction(result)
or
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
}
private int getLineRank() {
shouldGenerateDumpStrings() and
this =
rank[result](Instruction instr |
instr =
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1`
*/
string getResultId() {
shouldGenerateDumpStrings() and
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
}
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1(int*)`
*/
final string getResultString() {
shouldGenerateDumpStrings() and
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
}
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
* Example: `func:r3_4, this:r3_5`
*/
string getOperandsString() {
shouldGenerateDumpStrings() and
result =
concat(Operand operand |
operand = getAnOperand()
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
Construction::hasModeledMemoryResult(this)
}
/**
* Holds if this is an instruction with a memory result that represents a
* conflation of more than one memory allocation.
*
* This happens in practice when dereferencing a pointer that cannot be
* tracked back to a single local allocation. Such memory is instead modeled
* as originating on the `AliasedDefinitionInstruction` at the entry of the
* function.
*/
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
/**
* Gets the successor of this instruction along the control flow edge
* specified by `kind`.

View File

@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
override string toString() { result = "SideEffect" }
}
/**

View File

@@ -18,19 +18,19 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
predicate shouldPrintFunction(Language::Function func) { any() }
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
/**
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldCreateIRForFunction(Language::Function func) {
override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
shouldPrintFunction(func)
}
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
}

View File

@@ -1,2 +1,3 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SSAConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration

View File

@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.Opcode as Opcode
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap

View File

@@ -65,6 +65,29 @@ private module Cached {
instruction instanceof ChiInstruction // Chis always have modeled results
}
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
or
// Chi instructions track virtual variables, and therefore a chi instruction is
// conflated if it's associated with the aliased virtual variable.
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
Alias::AliasedVirtualVariable
)
or
// Phi instructions track locations, and therefore a phi instruction is
// conflated if it's associated with a conflated location.
exists(Alias::MemoryLocation location |
instruction = Phi(_, location) and
not exists(location.getAllocation())
)
}
cached
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand |
@@ -84,14 +107,15 @@ private module Cached {
oldOperand instanceof OldIR::NonPhiMemoryOperand and
exists(
OldBlock useBlock, int useRank, Alias::MemoryLocation useLocation,
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset,
Alias::MemoryLocation actualDefLocation
|
useLocation = Alias::getOperandMemoryLocation(oldOperand) and
hasUseAtRank(useLocation, useBlock, useRank, oldInstruction) and
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank) and
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, _) and
overlap = Alias::getOverlap(defLocation, useLocation)
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
)
}

View File

@@ -61,6 +61,11 @@ class MemoryLocation extends TMemoryLocation {
class VirtualVariable extends MemoryLocation { }
/** A virtual variable that groups all escaped memory within a function. */
class AliasedVirtualVariable extends VirtualVariable {
AliasedVirtualVariable() { none() }
}
Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
def = use and result instanceof MustExactlyOverlap
or

View File

@@ -363,6 +363,11 @@ CppPRValueType getIntType() {
*/
CppPRValueType getBoolType() { exists(BoolType type | result.hasType(type, false)) }
/**
* Gets the `CppType` that represents a glvalue of type `bool`.
*/
CppType getBoolGLValueType() { exists(BoolType type | result.hasType(type, true)) }
/**
* Gets the `CppType` that represents a glvalue of function type.
*/

View File

@@ -2,7 +2,8 @@ newtype TTempVariableTag =
ConditionValueTempVar() or
ReturnValueTempVar() or
ThrowTempVar() or
LambdaTempVar()
LambdaTempVar() or
EllipsisTempVar()
string getTempVariableTagId(TTempVariableTag tag) {
tag = ConditionValueTempVar() and result = "CondVal"
@@ -12,4 +13,6 @@ string getTempVariableTagId(TTempVariableTag tag) {
tag = ThrowTempVar() and result = "Throw"
or
tag = LambdaTempVar() and result = "Lambda"
or
tag = EllipsisTempVar() and result = "Ellipsis"
}

View File

@@ -1,6 +1,7 @@
private import implementations.Allocation
private import implementations.Deallocation
private import implementations.Fread
private import implementations.Gets
private import implementations.IdentityFunction
private import implementations.Inet
private import implementations.Memcpy

View File

@@ -0,0 +1,45 @@
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard functions `gets` and `fgets`.
*/
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
SideEffectFunction {
GetsFunction() {
exists(string name | hasGlobalOrStdName(name) |
name = "gets" or // gets(str)
name = "fgets" or // fgets(str, num, stream)
name = "fgetws" // fgetws(wstr, num, stream)
)
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(2) and
output.isParameterDeref(0)
}
override predicate parameterNeverEscapes(int index) { index = 2 }
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 0 and
buffer = true and
mustWrite = true
}
}

View File

@@ -1,11 +1,12 @@
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function `strcat` and its wide, sized, and Microsoft variants.
*/
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, SideEffectFunction {
StrcatFunction() {
exists(string name | name = getName() |
name = "strcat" or // strcat(dst, src)
@@ -56,4 +57,19 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
override predicate hasArrayWithNullTerminator(int param) { param = 1 }
override predicate hasArrayWithUnknownSize(int param) { param = 0 }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 0 and
buffer = true and
mustWrite = false
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
(i = 0 or i = 1) and
buffer = true
}
}

View File

@@ -1,11 +1,12 @@
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function `strcpy` and its wide, sized, and Microsoft variants.
*/
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction {
StrcpyFunction() {
this.hasName("strcpy") or
this.hasName("_mbscpy") or
@@ -74,4 +75,23 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
output.isReturnValueDeref()
)
}
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 0 and
buffer = true and
mustWrite = false
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
i = 1 and
buffer = true
}
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
hasArrayWithVariableSize(i, result)
}
}

View File

@@ -0,0 +1,34 @@
| args.cpp:8:5:8:12 | call to global_1 | 0 | 0 |
| args.cpp:9:5:9:12 | call to global_2 | 0 | 0 |
| args.cpp:9:5:9:12 | call to global_2 | 1 | 1 |
| args.cpp:10:5:10:19 | call to global_2_vararg | 0 | 0 |
| args.cpp:10:5:10:19 | call to global_2_vararg | 1 | 1 |
| args.cpp:11:5:11:19 | call to global_2_vararg | 0 | 0 |
| args.cpp:11:5:11:19 | call to global_2_vararg | 1 | 1 |
| args.cpp:11:5:11:19 | call to global_2_vararg | 2 | 2 |
| args.cpp:12:5:12:19 | call to global_2_vararg | 0 | 0 |
| args.cpp:12:5:12:19 | call to global_2_vararg | 1 | 1 |
| args.cpp:12:5:12:19 | call to global_2_vararg | 2 | 2 |
| args.cpp:12:5:12:19 | call to global_2_vararg | 3 | 2 |
| args.cpp:22:5:22:12 | call to expression | 0 | 0 |
| args.cpp:23:5:23:15 | call to expression | 0 | 0 |
| args.cpp:23:5:23:15 | call to expression | 1 | 1 |
| args.cpp:24:5:24:22 | call to expression | 0 | 0 |
| args.cpp:24:5:24:22 | call to expression | 1 | 1 |
| args.cpp:25:5:25:25 | call to expression | 0 | 0 |
| args.cpp:25:5:25:25 | call to expression | 1 | 1 |
| args.cpp:25:5:25:25 | call to expression | 2 | 2 |
| args.cpp:26:5:26:28 | call to expression | 0 | 0 |
| args.cpp:26:5:26:28 | call to expression | 1 | 1 |
| args.cpp:26:5:26:28 | call to expression | 2 | 2 |
| args.cpp:26:5:26:28 | call to expression | 3 | 2 |
| args.cpp:37:10:37:11 | call to S | 0 | 0 |
| args.cpp:38:19:38:23 | call to S | 0 | 0 |
| args.cpp:38:19:38:23 | call to S | 1 | 1 |
| args.cpp:39:19:39:26 | call to S | 0 | 0 |
| args.cpp:39:19:39:26 | call to S | 1 | 1 |
| args.cpp:39:19:39:26 | call to S | 2 | 2 |
| args.cpp:40:19:40:29 | call to S | 0 | 0 |
| args.cpp:40:19:40:29 | call to S | 1 | 1 |
| args.cpp:40:19:40:29 | call to S | 2 | 2 |
| args.cpp:40:19:40:29 | call to S | 3 | 2 |

View File

@@ -0,0 +1,6 @@
import cpp
import semmle.code.cpp.ir.implementation.raw.internal.VarArgs
from Call call, int argIndex, int paramIndex
where paramIndex = getParameterIndexForArgument(call, argIndex)
select call, argIndex, paramIndex

View File

@@ -0,0 +1,41 @@
void global_0();
void global_1(int a);
void global_2(int a, float b);
void global_2_vararg(int a, float b, ...);
void call_globals(int a, float b, void* c, bool d) {
global_0();
global_1(a);
global_2(a, b);
global_2_vararg(a, b);
global_2_vararg(a, b, c);
global_2_vararg(a, b, c, d);
}
void (*pfn_0)();
void (*pfn_1)(int a);
void (*pfn_2)(int a, float b);
void (*pfn_2_vararg)(int a, float b ...);
void call_pfns(int a, float b, void* c, bool d) {
pfn_0();
pfn_1(a);
pfn_2(a, b);
pfn_2_vararg(a, b);
pfn_2_vararg(a, b, c);
pfn_2_vararg(a, b, c, d);
}
struct S {
S();
S(int a);
S(int a, float b, ...);
};
void call_constructors(int a, float b, void* c, bool d) {
S s0;
S s1(a);
S s2_vararg_0(a, b);
S s2_vararg_1(a, b, c);
S s2_vararg_2(a, b, c, d);
}

View File

@@ -0,0 +1,31 @@
uniqueEnclosingCallable
uniqueTypeBound
uniqueTypeRepr
uniqueNodeLocation
| dispatch.cpp:60:18:60:29 | call to Bottom | Node should have one location but has 2. |
| dispatch.cpp:61:18:61:29 | call to Middle | Node should have one location but has 2. |
| dispatch.cpp:65:10:65:21 | call to Bottom | Node should have one location but has 2. |
| file://:0:0:0:0 | call to Bottom | Node should have one location but has 2. |
| file://:0:0:0:0 | call to Bottom | Node should have one location but has 2. |
| file://:0:0:0:0 | call to Middle | Node should have one location but has 2. |
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
compatibleTypesReflexive
unreachableNodeCCtx
localCallNodes
postIsNotPre
postHasUniquePre
uniquePostUpdate
postIsInSameCallable
reverseRead
storeIsPostUpdate
argHasPostUpdate
| dispatch.cpp:78:23:78:39 | * ... | ArgumentNode is missing PostUpdateNode. |
| lambdas.cpp:18:7:18:7 | a | ArgumentNode is missing PostUpdateNode. |
| lambdas.cpp:25:2:25:2 | b | ArgumentNode is missing PostUpdateNode. |
| lambdas.cpp:32:2:32:2 | c | ArgumentNode is missing PostUpdateNode. |
| lambdas.cpp:38:2:38:2 | d | ArgumentNode is missing PostUpdateNode. |
| lambdas.cpp:45:2:45:2 | e | ArgumentNode is missing PostUpdateNode. |

View File

@@ -0,0 +1 @@
import semmle.code.cpp.dataflow.internal.DataFlowImplConsistency::Consistency

View File

@@ -0,0 +1,36 @@
uniqueEnclosingCallable
uniqueTypeBound
uniqueTypeRepr
uniqueNodeLocation
| BarrierGuard.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. |
| acrossLinkTargets.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. |
| clang.cpp:4:11:4:13 | p#0 | Node should have one location but has 6. |
| clang.cpp:4:27:4:35 | p#0 | Node should have one location but has 2. |
| clang.cpp:4:51:4:53 | p#0 | Node should have one location but has 2. |
| dispatch.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| globals.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. |
| test.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. |
| test.cpp:2:27:2:35 | p#0 | Node should have one location but has 2. |
| test.cpp:2:51:2:53 | p#0 | Node should have one location but has 2. |
missingLocation
| Nodes without location: 4 |
uniqueNodeToString
| lambdas.cpp:2:6:2:9 | (no string representation) | Node should have one toString but has 0. |
missingToString
| Nodes without toString: 1 |
parameterCallable
localFlowIsLocal
compatibleTypesReflexive
unreachableNodeCCtx
localCallNodes
postIsNotPre
postHasUniquePre
uniquePostUpdate
postIsInSameCallable
reverseRead
storeIsPostUpdate
argHasPostUpdate

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.dataflow.internal.DataFlowImplConsistency::Consistency

View File

@@ -0,0 +1,58 @@
uniqueEnclosingCallable
| C.cpp:37:24:37:33 | 0 | Node should have one enclosing callable but has 0. |
| C.cpp:37:24:37:33 | new | Node should have one enclosing callable but has 0. |
uniqueTypeBound
| complex.cpp:22:11:22:17 | constructor init of field f [post-this] | Node should have one type bound but has 0. |
| complex.cpp:22:11:22:17 | constructor init of field f [pre-this] | Node should have one type bound but has 0. |
uniqueTypeRepr
| complex.cpp:22:11:22:17 | constructor init of field f [post-this] | Node should have one type representation but has 0. |
| complex.cpp:22:11:22:17 | constructor init of field f [pre-this] | Node should have one type representation but has 0. |
uniqueNodeLocation
| A.cpp:38:7:38:8 | call to C | Node should have one location but has 2. |
| A.cpp:39:7:39:8 | call to C | Node should have one location but has 2. |
| A.cpp:41:15:41:21 | call to C | Node should have one location but has 2. |
| A.cpp:47:12:47:18 | call to C | Node should have one location but has 2. |
| A.cpp:57:17:57:23 | call to C | Node should have one location but has 2. |
| A.cpp:64:21:64:28 | call to C2 | Node should have one location but has 2. |
| A.cpp:73:25:73:32 | call to C2 | Node should have one location but has 2. |
| A.cpp:126:12:126:18 | call to C | Node should have one location but has 2. |
| A.cpp:142:14:142:20 | call to C | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C2 | Node should have one location but has 2. |
| file://:0:0:0:0 | call to C2 | Node should have one location but has 2. |
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
compatibleTypesReflexive
unreachableNodeCCtx
localCallNodes
postIsNotPre
postHasUniquePre
uniquePostUpdate
postIsInSameCallable
reverseRead
storeIsPostUpdate
argHasPostUpdate
| A.cpp:41:15:41:21 | new | ArgumentNode is missing PostUpdateNode. |
| A.cpp:55:12:55:19 | new | ArgumentNode is missing PostUpdateNode. |
| A.cpp:57:17:57:23 | new | ArgumentNode is missing PostUpdateNode. |
| A.cpp:64:21:64:28 | new | ArgumentNode is missing PostUpdateNode. |
| A.cpp:73:25:73:32 | new | ArgumentNode is missing PostUpdateNode. |
| A.cpp:126:12:126:18 | new | ArgumentNode is missing PostUpdateNode. |
| A.cpp:160:32:160:59 | new | ArgumentNode is missing PostUpdateNode. |
| D.cpp:29:24:29:40 | new | ArgumentNode is missing PostUpdateNode. |
| D.cpp:36:24:36:40 | new | ArgumentNode is missing PostUpdateNode. |
| D.cpp:43:24:43:40 | new | ArgumentNode is missing PostUpdateNode. |
| D.cpp:50:24:50:40 | new | ArgumentNode is missing PostUpdateNode. |
| D.cpp:57:25:57:41 | new | ArgumentNode is missing PostUpdateNode. |
| by_reference.cpp:51:8:51:8 | s | ArgumentNode is missing PostUpdateNode. |
| by_reference.cpp:57:8:57:8 | s | ArgumentNode is missing PostUpdateNode. |
| by_reference.cpp:63:8:63:8 | s | ArgumentNode is missing PostUpdateNode. |

View File

@@ -0,0 +1 @@
import semmle.code.cpp.dataflow.internal.DataFlowImplConsistency::Consistency

View File

@@ -0,0 +1,26 @@
uniqueEnclosingCallable
uniqueTypeBound
uniqueTypeRepr
uniqueNodeLocation
| D.cpp:1:17:1:17 | o | Node should have one location but has 2. |
| by_reference.cpp:1:17:1:17 | o | Node should have one location but has 2. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
missingLocation
| Nodes without location: 4 |
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
compatibleTypesReflexive
unreachableNodeCCtx
localCallNodes
postIsNotPre
postHasUniquePre
uniquePostUpdate
postIsInSameCallable
reverseRead
storeIsPostUpdate
argHasPostUpdate

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.dataflow.internal.DataFlowImplConsistency::Consistency

View File

@@ -67,3 +67,9 @@
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | |
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | |
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets | |
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | |
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | |
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer | |

View File

@@ -11,3 +11,6 @@
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | AST only |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | AST only |
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | AST only |
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | AST only |
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | IR only |

View File

@@ -52,3 +52,8 @@
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | |
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets | |
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | |
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | |
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer | |

View File

@@ -88,4 +88,14 @@ void mallocBuffer() {
if (!strcmp(copy, "admin")) { // copy should be tainted
isAdmin = true;
}
}
}
char *gets(char *s);
void test_gets()
{
char buffer[1024];
char *pointer;
pointer = gets(buffer);
}

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,9 @@ lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
switchInstructionWithoutDefaultEdge
notMarkedAsConflated
wronglyMarkedAsConflated
invalidOverlap
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType

View File

@@ -20,6 +20,9 @@ lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
switchInstructionWithoutDefaultEdge
notMarkedAsConflated
wronglyMarkedAsConflated
invalidOverlap
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType

View File

@@ -885,15 +885,24 @@ void FuncPtrConversions(int(*pfn)(int), void* p) {
pfn = (int(*)(int))p;
}
void VAListUsage(int x, __builtin_va_list args) {
__builtin_va_list args2;
__builtin_va_copy(args2, args);
double d = __builtin_va_arg(args, double);
float f = __builtin_va_arg(args, int);
__builtin_va_end(args2);
}
void VarArgUsage(int x, ...) {
__builtin_va_list args;
__builtin_va_start(args, x);
__builtin_va_list args2;
__builtin_va_start(args2, args);
__builtin_va_copy(args2, args);
double d = __builtin_va_arg(args, double);
float f = __builtin_va_arg(args, float);
float f = __builtin_va_arg(args, int);
__builtin_va_end(args);
VAListUsage(x, args2);
__builtin_va_end(args2);
}
@@ -1219,4 +1228,31 @@ void switch2Case_default(int x) {
int z = y;
}
int staticLocalInit(int x) {
static int a = 0; // Constant initialization
static int b = sizeof(x); // Constant initialization
static int c = x; // Dynamic initialization
static int d; // Zero initialization
return a + b + c + d;
}
void staticLocalWithConstructor(const char* dynamic) {
static String a;
static String b("static");
static String c(dynamic);
}
// --- strings ---
char *strcpy(char *destination, const char *source);
char *strcat(char *destination, const char *source);
void test_strings(char *s1, char *s2) {
char buffer[1024] = {0};
strcpy(buffer, s1);
strcat(buffer, s2);
}
// semmle-extractor-options: -std=c++17 --clang

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,9 @@ lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
switchInstructionWithoutDefaultEdge
notMarkedAsConflated
wronglyMarkedAsConflated
invalidOverlap
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType

View File

@@ -20,6 +20,9 @@ lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
switchInstructionWithoutDefaultEdge
notMarkedAsConflated
wronglyMarkedAsConflated
invalidOverlap
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType

View File

@@ -20,6 +20,9 @@ lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
switchInstructionWithoutDefaultEdge
notMarkedAsConflated
wronglyMarkedAsConflated
invalidOverlap
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType

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