Merge branch 'main' into break-bigstep-at-store

This commit is contained in:
Mathias Vorreiter Pedersen
2024-04-16 15:33:21 +01:00
402 changed files with 5371 additions and 17855 deletions

View File

@@ -0,0 +1,556 @@
/**
* INTERNAL use only. This is an experimental API subject to change without notice.
*
* Provides classes and predicates for dealing with flow models specified in CSV format.
*
* The CSV specification has the following columns:
* - Sources:
* `namespace; type; subtypes; name; signature; ext; output; kind`
* - Sinks:
* `namespace; type; subtypes; name; signature; ext; input; kind`
* - Summaries:
* `namespace; type; subtypes; name; signature; ext; input; output; kind`
*
* The interpretation of a row is similar to API-graphs with a left-to-right
* reading.
* 1. The `namespace` column selects a namespace.
* 2. The `type` column selects a type within that namespace.
* 3. The `subtypes` is a boolean that indicates whether to jump to an
* arbitrary subtype of that type. Set this to `false` if leaving the `type`
* blank (for example, a free function).
* 4. The `name` column optionally selects a specific named member of the type.
* 5. The `signature` column optionally restricts the named member. If
* `signature` is blank then no such filtering is done. The format of the
* signature is a comma-separated list of types enclosed in parentheses. The
* types can be short names or fully qualified names (mixing these two options
* is not allowed within a single signature).
* 6. The `ext` column specifies additional API-graph-like edges. Currently
* there is only one valid value: "".
* 7. The `input` column specifies how data enters the element selected by the
* first 6 columns, and the `output` column specifies how data leaves the
* element selected by the first 6 columns. An `input` can be either:
* - "": Selects a write to the selected element in case this is a field.
* - "Argument[n]": Selects an argument in a call to the selected element.
* The arguments are zero-indexed, and `-1` specifies the qualifier object,
* that is, `*this`.
* - one or more "*" can be added in front of the argument index to indicate
* indirection, for example, `Argument[*0]` indicates the first indirection
* of the 0th argument.
* - `n1..n2` syntax can be used to indicate a range of arguments, inclusive
* at both ends. One or more "*"s can be added in front of the whole range
* to indicate that every argument in the range is indirect, for example
* `*0..1` is the first indirection of both arguments 0 and 1.
* - "ReturnValue": Selects a value being returned by the selected element.
* One or more "*" can be added as an argument to indicate indirection, for
* example, "ReturnValue[*]" indicates the first indirection of the return
* value.
*
* An `output` can be either:
* - "": Selects a read of a selected field.
* - "Argument[n]": Selects the post-update value of an argument in a call to
* the selected element. That is, the value of the argument after the call
* returns. The arguments are zero-indexed, and `-1` specifies the qualifier
* object, that is, `*this`.
* - one or more "*" can be added in front of the argument index to indicate
* indirection, for example, `Argument[*0]` indicates the first indirection
* of the 0th argument.
* - `n1..n2` syntax can be used to indicate a range of arguments, inclusive
* at both ends. One or more "*"s can be added in front of the whole range
* to indicate that every argument in the range is indirect, for example
* `*0..1` is the first indirection of both arguments 0 and 1.
* - "Parameter[n]": Selects the value of a parameter of the selected element.
* The syntax is the same as for "Argument", for example "Parameter[0]",
* "Parameter[*0]", "Parameter[0..2]" etc.
* - "ReturnValue": Selects a value being returned by the selected element.
* One or more "*" can be added as an argument to indicate indirection, for
* example, "ReturnValue[*]" indicates the first indirection of the return
* value.
* 8. The `kind` column is a tag that can be referenced from QL to determine to
* which classes the interpreted elements should be added. For example, for
* sources "remote" indicates a default remote flow source, and for summaries
* "taint" indicates a default additional taint step and "value" indicates a
* globally applicable value-preserving step.
*/
import cpp
private import new.DataFlow
private import internal.FlowSummaryImpl
private import internal.FlowSummaryImpl::Public
private import internal.FlowSummaryImpl::Private
private import internal.FlowSummaryImpl::Private::External
private import codeql.mad.ModelValidation as SharedModelVal
private import codeql.util.Unit
/**
* A unit class for adding additional source model rows.
*
* Extend this class to add additional source definitions.
*/
class SourceModelCsv extends Unit {
/** Holds if `row` specifies a source definition. */
abstract predicate row(string row);
}
/**
* A unit class for adding additional sink model rows.
*
* Extend this class to add additional sink definitions.
*/
class SinkModelCsv extends Unit {
/** Holds if `row` specifies a sink definition. */
abstract predicate row(string row);
}
/**
* A unit class for adding additional summary model rows.
*
* Extend this class to add additional flow summary definitions.
*/
class SummaryModelCsv extends Unit {
/** Holds if `row` specifies a summary definition. */
abstract predicate row(string row);
}
/** Holds if `row` is a source model. */
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
/** Holds if `row` is a sink model. */
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
/** Holds if `row` is a summary model. */
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
/** Holds if a source model exists for the given parameters. */
predicate sourceModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string output, string kind, string provenance
) {
exists(string row |
sourceModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = output and
row.splitAt(";", 7) = kind
) and
provenance = "manual"
}
/** Holds if a sink model exists for the given parameters. */
predicate sinkModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string kind, string provenance
) {
exists(string row |
sinkModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = input and
row.splitAt(";", 7) = kind
) and
provenance = "manual"
}
/** Holds if a summary model exists for the given parameters. */
predicate summaryModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string output, string kind, string provenance
) {
exists(string row |
summaryModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = input and
row.splitAt(";", 7) = output and
row.splitAt(";", 8) = kind
) and
provenance = "manual"
}
private predicate relevantNamespace(string namespace) {
sourceModel(namespace, _, _, _, _, _, _, _, _) or
sinkModel(namespace, _, _, _, _, _, _, _, _) or
summaryModel(namespace, _, _, _, _, _, _, _, _, _)
}
private predicate namespaceLink(string shortns, string longns) {
relevantNamespace(shortns) and
relevantNamespace(longns) and
longns.prefix(longns.indexOf("::")) = shortns
}
private predicate canonicalNamespace(string namespace) {
relevantNamespace(namespace) and not namespaceLink(_, namespace)
}
private predicate canonicalNamespaceLink(string namespace, string subns) {
canonicalNamespace(namespace) and
(subns = namespace or namespaceLink(namespace, subns))
}
/**
* Holds if CSV framework coverage of `namespace` is `n` api endpoints of the
* kind `(kind, part)`.
*/
predicate modelCoverage(string namespace, int namespaces, string kind, string part, int n) {
namespaces = strictcount(string subns | canonicalNamespaceLink(namespace, subns)) and
(
part = "source" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
string ext, string output, string provenance |
canonicalNamespaceLink(namespace, subns) and
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance)
)
or
part = "sink" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
string ext, string input, string provenance |
canonicalNamespaceLink(namespace, subns) and
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance)
)
or
part = "summary" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
string ext, string input, string output, string provenance |
canonicalNamespaceLink(namespace, subns) and
summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, provenance)
)
)
}
/** Provides a query predicate to check the CSV data for validation errors. */
module CsvValidation {
private string getInvalidModelInput() {
exists(string pred, AccessPath input, string part |
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
or
summaryModel(_, _, _, _, _, _, input, _, _, _) and pred = "summary"
|
(
invalidSpecComponent(input, part) and
not part = "" and
not (part = "Argument" and pred = "sink") and
not parseArg(part, _)
or
part = input.getToken(_) and
parseParam(part, _)
) and
result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
)
}
private string getInvalidModelOutput() {
exists(string pred, string output, string part |
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
or
summaryModel(_, _, _, _, _, _, _, output, _, _) and pred = "summary"
|
invalidSpecComponent(output, part) and
not part = "" and
not (part = ["Argument", "Parameter"] and pred = "source") and
result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
)
}
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) }
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) }
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) }
}
private module KindVal = SharedModelVal::KindValidation<KindValConfig>;
private string getInvalidModelSubtype() {
exists(string pred, string row |
sourceModel(row) and pred = "source"
or
sinkModel(row) and pred = "sink"
or
summaryModel(row) and pred = "summary"
|
exists(string b |
b = row.splitAt(";", 2) and
not b = ["true", "false"] and
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
)
)
}
private string getInvalidModelColumnCount() {
exists(string pred, string row, int expect |
sourceModel(row) and expect = 8 and pred = "source"
or
sinkModel(row) and expect = 8 and pred = "sink"
or
summaryModel(row) and expect = 9 and pred = "summary"
|
exists(int cols |
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
cols != expect and
result =
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
"."
)
)
}
private string getInvalidModelSignature() {
exists(string pred, string namespace, string type, string name, string signature, string ext |
sourceModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "source"
or
sinkModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "sink"
or
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
|
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
or
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and
result = "Dubious type \"" + type + "\" in " + pred + " model."
or
not name.regexpMatch("[a-zA-Z0-9_<>,]*") and
result = "Dubious member name \"" + name + "\" in " + pred + " model."
or
not signature.regexpMatch("|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)") and
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
or
not ext.regexpMatch("|Attribute") and
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
)
}
/** Holds if some row in a CSV-based flow model appears to contain typos. */
query predicate invalidModelRow(string msg) {
msg =
[
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
]
}
}
private predicate elementSpec(
string namespace, string type, boolean subtypes, string name, string signature, string ext
) {
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
}
private string paramsStringPart(Function c, int i) {
i = -1 and result = "(" and exists(c)
or
exists(int n, string p | c.getParameter(n).getType().toString() = p |
i = 2 * n and result = p
or
i = 2 * n - 1 and result = "," and n != 0
)
or
i = 2 * c.getNumberOfParameters() and result = ")"
}
/**
* Gets a parenthesized string containing all parameter types of this callable, separated by a comma.
*
* Returns the empty string if the callable has no parameters.
* Parameter types are represented by their type erasure.
*/
cached
private string paramsString(Function c) {
result = concat(int i | | paramsStringPart(c, i) order by i)
}
bindingset[func]
private predicate matchesSignature(Function func, string signature) {
signature = "" or
paramsString(func) = signature
}
/**
* Gets the element in module `namespace` that satisfies the following properties:
* 1. If the element is a member of a class-like type, then the class-like type has name `type`
* 2. If `subtypes = true` and the element is a member of a class-like type, then overrides of the element
* are also returned.
* 3. The element has name `name`
* 4. If `signature` is non-empty, then the element has a list of parameter types described by `signature`.
*
* NOTE: `namespace` is currently not used (since we don't properly extract modules yet).
*/
pragma[nomagic]
private Element interpretElement0(
string namespace, string type, boolean subtypes, string name, string signature
) {
elementSpec(namespace, type, subtypes, name, signature, _) and
(
// Non-member functions
exists(Function func |
func.hasQualifiedName(namespace, name) and
type = "" and
matchesSignature(func, signature) and
subtypes = false and
not exists(func.getDeclaringType()) and
result = func
)
or
// Member functions
exists(Class namedClass, Class classWithMethod, Function method |
classWithMethod = method.getClassAndName(name) and
namedClass.hasQualifiedName(namespace, type) and
matchesSignature(method, signature) and
result = method
|
// member declared in the named type or a subtype of it
subtypes = true and
classWithMethod = namedClass.getADerivedClass*()
or
// member declared directly in the named type
subtypes = false and
classWithMethod = namedClass
)
or
// Member variables
signature = "" and
exists(Class namedClass, Class classWithMember, MemberVariable member |
member.getName() = name and
member = classWithMember.getAMember() and
namedClass.hasQualifiedName(namespace, type) and
result = member
|
// field declared in the named type or a subtype of it (or an extension of any)
subtypes = true and
classWithMember = namedClass.getADerivedClass*()
or
// field declared directly in the named type (or an extension of it)
subtypes = false and
classWithMember = namedClass
)
or
// Global or namespace variables
signature = "" and
type = "" and
subtypes = false and
result = any(GlobalOrNamespaceVariable v | v.hasQualifiedName(namespace, name))
)
}
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
Element interpretElement(
string namespace, string type, boolean subtypes, string name, string signature, string ext
) {
elementSpec(namespace, type, subtypes, name, signature, ext) and
exists(Element e | e = interpretElement0(namespace, type, subtypes, name, signature) |
ext = "" and result = e
)
}
cached
private module Cached {
/**
* Holds if `node` is specified as a source with the given kind in a CSV flow
* model.
*/
cached
predicate sourceNode(DataFlow::Node node, string kind) {
exists(SourceSinkInterpretationInput::InterpretNode n |
isSourceNode(n, kind, _) and n.asNode() = node // TODO
)
}
/**
* Holds if `node` is specified as a sink with the given kind in a CSV flow
* model.
*/
cached
predicate sinkNode(DataFlow::Node node, string kind) {
exists(SourceSinkInterpretationInput::InterpretNode n |
isSinkNode(n, kind, _) and n.asNode() = node // TODO
)
}
}
import Cached
private predicate interpretSummary(
Function f, string input, string output, string kind, string provenance
) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
f = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
// adapter class for converting Mad summaries to `SummarizedCallable`s
private class SummarizedCallableAdapter extends SummarizedCallable {
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _) }
private predicate relevantSummaryElementManual(string input, string output, string kind) {
exists(Provenance provenance |
interpretSummary(this, input, output, kind, provenance) and
provenance.isManual()
)
}
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
exists(Provenance provenance |
interpretSummary(this, input, output, kind, provenance) and
provenance.isGenerated()
)
}
override predicate propagatesFlow(
string input, string output, boolean preservesValue, string model
) {
exists(string kind |
this.relevantSummaryElementManual(input, output, kind)
or
not this.relevantSummaryElementManual(_, _, _) and
this.relevantSummaryElementGenerated(input, output, kind)
|
if kind = "value" then preservesValue = true else preservesValue = false
) and
model = "" // TODO
}
override predicate hasProvenance(Provenance provenance) {
interpretSummary(this, _, _, _, provenance)
}
}
// adapter class for converting Mad neutrals to `NeutralCallable`s
private class NeutralCallableAdapter extends NeutralCallable {
string kind;
string provenance_;
NeutralCallableAdapter() {
// Neutral models have not been implemented for CPP.
none() and
exists(this) and
exists(kind) and
exists(provenance_)
}
override string getKind() { result = kind }
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
}

View File

@@ -0,0 +1,271 @@
/**
* Provides classes and predicates for defining flow summaries.
*/
private import cpp as Cpp
private import codeql.dataflow.internal.FlowSummaryImpl
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific as DataFlowImplSpecific
private import semmle.code.cpp.dataflow.ExternalFlow
private import semmle.code.cpp.ir.IR
module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
class SummarizedCallableBase = Function;
ArgumentPosition callbackSelfParameterPosition() { result = TDirectPosition(-1) }
ReturnKind getStandardReturnValueKind() { result.(NormalReturnKind).getIndirectionIndex() = 0 }
string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() }
string encodeArgumentPosition(ArgumentPosition pos) { result = pos.toString() }
string encodeReturn(ReturnKind rk, string arg) {
rk != getStandardReturnValueKind() and
result = "ReturnValue" and
arg = repeatStars(rk.(NormalReturnKind).getIndirectionIndex())
}
string encodeContent(ContentSet cs, string arg) {
exists(FieldContent c |
cs.isSingleton(c) and
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
result = "Field" and
arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName()
)
}
string encodeWithoutContent(ContentSet c, string arg) {
// used for type tracking, not currently used in C/C++.
result = "WithoutContent" + c and arg = ""
}
string encodeWithContent(ContentSet c, string arg) {
// used for type tracking, not currently used in C/C++.
result = "WithContent" + c and arg = ""
}
/**
* Decodes an argument / parameter position string, for example the `0` in `Argument[0]`.
* Supports ranges (`Argument[x..y]`), qualifiers (`Argument[-1]`), indirections
* (`Argument[*x]`) and combinations (such as `Argument[**0..1]`).
*/
bindingset[argString]
private TPosition decodePosition(string argString) {
exists(int indirection, string posString, int pos |
argString = repeatStars(indirection) + posString and
pos = AccessPath::parseInt(posString) and
(
pos >= 0 and indirection = 0 and result = TDirectPosition(pos)
or
pos >= 0 and indirection > 0 and result = TIndirectionPosition(pos, indirection)
or
// `Argument[-1]` / `Parameter[-1]` is the qualifier object `*this`, not the `this` pointer itself.
pos = -1 and result = TIndirectionPosition(pos, indirection + 1)
)
)
}
bindingset[token]
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
token.getName() = "Argument" and
result = decodePosition(token.getAnArgument())
}
bindingset[token]
ArgumentPosition decodeUnknownArgumentPosition(AccessPath::AccessPathTokenBase token) {
token.getName() = "Parameter" and
result = decodePosition(token.getAnArgument())
}
bindingset[token]
ContentSet decodeUnknownContent(AccessPath::AccessPathTokenBase token) {
// field content (no indirection support)
exists(FieldContent c |
result.isSingleton(c) and
token.getName() = c.getField().getName() and
not exists(token.getArgumentList()) and
c.getIndirectionIndex() = 1
)
or
// field content (with indirection support)
exists(FieldContent c |
result.isSingleton(c) and
token.getName() = c.getField().getName() and
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
token.getAnArgument() = repeatStars(c.getIndirectionIndex() - 1)
)
}
}
private import Make<Location, DataFlowImplSpecific::CppDataFlow, Input> as Impl
private module StepsInput implements Impl::Private::StepsInputSig {
DataFlowCall getACall(Public::SummarizedCallable sc) {
result.getStaticCallTarget().getUnderlyingCallable() = sc
}
}
module SourceSinkInterpretationInput implements
Impl::Private::External::SourceSinkInterpretationInputSig
{
class Element = Cpp::Element;
class SourceOrSinkElement = Element;
/**
* Holds if an external source specification exists for `e` with output specification
* `output`, kind `kind`, and provenance `provenance`.
*/
predicate sourceElement(
SourceOrSinkElement e, string output, string kind, Public::Provenance provenance, string model
) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
e = interpretElement(namespace, type, subtypes, name, signature, ext) and
model = "" // TODO
)
}
/**
* Holds if an external sink specification exists for `e` with input specification
* `input`, kind `kind` and provenance `provenance`.
*/
predicate sinkElement(
SourceOrSinkElement e, string input, string kind, Public::Provenance provenance, string model
) {
exists(
string package, string type, boolean subtypes, string name, string signature, string ext
|
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
e = interpretElement(package, type, subtypes, name, signature, ext) and
model = "" // TODO
)
}
private newtype TInterpretNode =
TElement_(Element n) or
TNode_(Node n)
/** An entity used to interpret a source/sink specification. */
class InterpretNode extends TInterpretNode {
/** Gets the element that this node corresponds to, if any. */
SourceOrSinkElement asElement() { this = TElement_(result) }
/** Gets the data-flow node that this node corresponds to, if any. */
Node asNode() { this = TNode_(result) }
/** Gets the call that this node corresponds to, if any. */
DataFlowCall asCall() {
this.asElement() = result.asCallInstruction().getUnconvertedResultExpression()
}
/** Gets the callable that this node corresponds to, if any. */
DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() }
/** Gets the target of this call, if any. */
Element getCallTarget() { result = this.asCall().getStaticCallTarget().getUnderlyingCallable() }
/** Gets a textual representation of this node. */
string toString() {
result = this.asElement().toString()
or
result = this.asNode().toString()
or
result = this.asCall().toString()
}
/** Gets the location of this node. */
Location getLocation() {
result = this.asElement().getLocation()
or
result = this.asNode().getLocation()
or
result = this.asCall().getLocation()
}
}
/** Provides additional sink specification logic. */
bindingset[c]
predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) {
// Allow variables to be picked as output nodes.
exists(Node n, Element ast |
n = node.asNode() and
ast = mid.asElement()
|
c = "" and
n.asExpr().(VariableAccess).getTarget() = ast
)
}
/** Provides additional source specification logic. */
bindingset[c]
predicate interpretInput(string c, InterpretNode mid, InterpretNode node) {
exists(Node n, Element ast, VariableAccess e |
n = node.asNode() and
ast = mid.asElement() and
e.getTarget() = ast
|
// Allow variables to be picked as input nodes.
// We could simply do this as `e = n.asExpr()`, but that would not allow
// us to pick `x` as a sink in an example such as `x = source()` (but
// only subsequent uses of `x`) since the variable access on `x` doesn't
// actually load the value of `x`. So instead, we pick the instruction
// node corresponding to the generated `StoreInstruction` and use the
// expression associated with the destination instruction. This means
// that the `x` in `x = source()` can be marked as an input.
c = "" and
exists(StoreInstruction store |
store.getDestinationAddress().getUnconvertedResultExpression() = e and
n.asInstruction() = store
)
)
}
}
module Private {
import Impl::Private
module Steps = Impl::Private::Steps<StepsInput>;
module External {
import Impl::Private::External
import Impl::Private::External::SourceSinkInterpretation<SourceSinkInterpretationInput>
}
/**
* Provides predicates for constructing summary components.
*/
module SummaryComponent {
private import Impl::Private::SummaryComponent as SC
predicate parameter = SC::parameter/1;
predicate argument = SC::argument/1;
predicate content = SC::content/1;
predicate withoutContent = SC::withoutContent/1;
predicate withContent = SC::withContent/1;
}
/**
* Provides predicates for constructing stacks of summary components.
*/
module SummaryComponentStack {
private import Impl::Private::SummaryComponentStack as SCS
predicate singleton = SCS::singleton/1;
predicate push = SCS::push/2;
predicate argument = SCS::argument/1;
}
}
module Public = Impl::Public;

View File

@@ -7,6 +7,7 @@ import cpp
private import semmle.code.cpp.ir.ValueNumbering
private import internal.DataFlowDispatch
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
/**
* Resolve potential target function(s) for `call`.
@@ -16,8 +17,9 @@ private import semmle.code.cpp.ir.IR
* to identify the possible target(s).
*/
Function resolveCall(Call call) {
exists(CallInstruction callInstruction |
exists(DataFlowCall dataFlowCall, CallInstruction callInstruction |
callInstruction.getAst() = call and
result = viableCallable(callInstruction)
callInstruction = dataFlowCall.asCallInstruction() and
result = viableCallable(dataFlowCall).getUnderlyingCallable()
)
}

View File

@@ -23,13 +23,13 @@ DataFlowCallable defaultViableCallable(DataFlowCall call) {
// function with the right signature is present in the database, we return
// that as a potential callee.
exists(string qualifiedName, int nparams |
callSignatureWithoutBody(qualifiedName, nparams, call) and
functionSignatureWithBody(qualifiedName, nparams, result) and
callSignatureWithoutBody(qualifiedName, nparams, call.asCallInstruction()) and
functionSignatureWithBody(qualifiedName, nparams, result.getUnderlyingCallable()) and
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
)
or
// Virtual dispatch
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
result.asSourceCallable() = call.(VirtualDispatch::DataSensitiveCall).resolve()
}
/**
@@ -40,7 +40,9 @@ DataFlowCallable viableCallable(DataFlowCall call) {
result = defaultViableCallable(call)
or
// Additional call targets
result = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
result.getUnderlyingCallable() =
any(AdditionalCallTarget additional)
.viableTarget(call.asCallInstruction().getUnconvertedResultExpression())
}
/**
@@ -150,7 +152,7 @@ private module VirtualDispatch {
ReturnNode node, ReturnKind kind, DataFlowCallable callable
) {
node.getKind() = kind and
node.getEnclosingCallable() = callable
node.getEnclosingCallable() = callable.getUnderlyingCallable()
}
/** Call through a function pointer. */
@@ -176,10 +178,15 @@ private module VirtualDispatch {
/** Call to a virtual function. */
private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
DataSensitiveOverriddenFunctionCall() {
exists(this.getStaticCallTarget().(VirtualFunction).getAnOverridingFunction())
exists(
this.getStaticCallTarget()
.getUnderlyingCallable()
.(VirtualFunction)
.getAnOverridingFunction()
)
}
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getThisArgument() }
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getArgument(-1) }
override MemberFunction resolve() {
exists(Class overridingClass |
@@ -194,7 +201,8 @@ private module VirtualDispatch {
*/
pragma[noinline]
private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) {
overridingFunction.getAnOverriddenFunction+() = this.getStaticCallTarget().(VirtualFunction) and
overridingFunction.getAnOverriddenFunction+() =
this.getStaticCallTarget().getUnderlyingCallable().(VirtualFunction) and
overridingFunction.getDeclaringType() = overridingClass
}
@@ -256,12 +264,12 @@ predicate mayBenefitFromCallContext(DataFlowCall call) { mayBenefitFromCallConte
* value is given as the `arg`'th argument to `f`.
*/
private predicate mayBenefitFromCallContext(
VirtualDispatch::DataSensitiveCall call, Function f, int arg
VirtualDispatch::DataSensitiveCall call, DataFlowCallable f, int arg
) {
f = pragma[only_bind_out](call).getEnclosingCallable() and
exists(InitializeParameterInstruction init |
not exists(call.getStaticCallTarget()) and
init.getEnclosingFunction() = f and
init.getEnclosingFunction() = f.getUnderlyingCallable() and
call.flowsFrom(DataFlow::instructionNode(init), _) and
init.getParameter().getIndex() = arg
)
@@ -273,10 +281,11 @@ private predicate mayBenefitFromCallContext(
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
result = viableCallable(call) and
exists(int i, Function f |
exists(int i, DataFlowCallable f |
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
f = ctx.getStaticCallTarget() and
result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
result.asSourceCallable() =
ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
)
}

View File

@@ -3,6 +3,7 @@ private import DataFlowUtil
private import semmle.code.cpp.ir.IR
private import DataFlowDispatch
private import semmle.code.cpp.ir.internal.IRCppLanguage
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import SsaInternals as Ssa
private import DataFlowImplCommon as DataFlowImplCommon
private import codeql.util.Unit
@@ -79,15 +80,6 @@ module NodeStars {
result = n.(FinalParameterNode).getIndirectionIndex()
}
private int maxNumberOfIndirections() { result = max(getNumberOfIndirections(_)) }
private string repeatStars(int n) {
n = 0 and result = ""
or
n = [1 .. maxNumberOfIndirections()] and
result = "*" + repeatStars(n - 1)
}
/**
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
* output for `n`.
@@ -97,6 +89,11 @@ module NodeStars {
import NodeStars
/**
* A cut-down `DataFlow::Node` class that does not depend on the output of SSA.
* This can thus be safely used in the SSA computations themselves, as well as
* in construction of other node classes (`TIRDataFlowNode`).
*/
class Node0Impl extends TIRDataFlowNode0 {
/**
* INTERNAL: Do not use.
@@ -333,7 +330,9 @@ private module IndirectInstructions {
import IndirectInstructions
/** Gets the callable in which this node occurs. */
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
DataFlowCallable nodeGetEnclosingCallable(Node n) {
result.getUnderlyingCallable() = n.getEnclosingCallable()
}
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
@@ -379,12 +378,30 @@ private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode
override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
exists(int indirectionIndex |
pos = TIndirectionPosition(argumentIndex, pragma[only_bind_into](indirectionIndex)) and
this.getCallInstruction() = dfCall and
this.getCallInstruction() = dfCall.asCallInstruction() and
super.hasAddressOperandAndIndirectionIndex(_, pragma[only_bind_into](indirectionIndex))
)
}
}
/**
* An argument node that is part of a summary. These only occur when the
* summary contains a synthesized call.
*/
class SummaryArgumentNode extends ArgumentNode, FlowSummaryNode {
private SummaryCall call_;
private ArgumentPosition pos_;
SummaryArgumentNode() {
FlowSummaryImpl::Private::summaryArgumentNode(call_.getReceiver(), this.getSummaryNode(), pos_)
}
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
call = call_ and
pos = pos_
}
}
/** A parameter position represented by an integer. */
class ParameterPosition = Position;
@@ -432,24 +449,42 @@ class IndirectionPosition extends Position, TIndirectionPosition {
}
newtype TPosition =
TDirectPosition(int index) { exists(any(CallInstruction c).getArgument(index)) } or
TDirectPosition(int argumentIndex) { exists(any(CallInstruction c).getArgument(argumentIndex)) } or
TIndirectionPosition(int argumentIndex, int indirectionIndex) {
hasOperandAndIndex(_, any(CallInstruction call).getArgumentOperand(argumentIndex),
Ssa::hasIndirectOperand(any(CallInstruction call).getArgumentOperand(argumentIndex),
indirectionIndex)
}
private newtype TReturnKind =
TNormalReturnKind(int index) {
exists(IndirectReturnNode return |
return.isNormalReturn() and
index = return.getIndirectionIndex() - 1 // We subtract one because the return loads the value.
)
TNormalReturnKind(int indirectionIndex) {
// derive a possible return indirection from SSA
// (this is a more durable approach if SSA infers additional indirections for any reason)
Ssa::hasIndirectOperand(any(ReturnValueInstruction ret).getReturnAddressOperand(),
indirectionIndex + 1) // We subtract one because the return loads the value.
or
// derive a possible return kind from the AST
// (this approach includes functions declared that have no body; they may still have flow summaries)
indirectionIndex =
[0 .. max(Cpp::Function f |
not exists(f.getBlock())
|
Ssa::getMaxIndirectionsForType(f.getUnspecifiedType()) - 1 // -1 because a returned value is a prvalue not a glvalue
)]
} or
TIndirectReturnKind(int argumentIndex, int indirectionIndex) {
exists(IndirectReturnNode return |
return.isParameterReturn(argumentIndex) and
indirectionIndex = return.getIndirectionIndex()
// derive a possible return argument from SSA
exists(Ssa::FinalParameterUse use |
use.getIndirectionIndex() = indirectionIndex and
use.getArgumentIndex() = argumentIndex
)
or
// derive a possible return argument from the AST
indirectionIndex =
[0 .. max(Cpp::Function f |
not exists(f.getBlock())
|
Ssa::getMaxIndirectionsForType(f.getParameter(argumentIndex).getUnspecifiedType()) - 1 // -1 because an argument is a prvalue not a glvalue
)]
}
/**
@@ -457,29 +492,44 @@ private newtype TReturnKind =
* from a callable. For C++, this is simply a function return.
*/
class ReturnKind extends TReturnKind {
/**
* Gets the indirection index of this return kind.
*/
abstract int getIndirectionIndex();
/** Gets a textual representation of this return kind. */
abstract string toString();
}
private class NormalReturnKind extends ReturnKind, TNormalReturnKind {
int index;
/**
* A value returned from a callable using a `return` statement, that is, a "normal" return.
*/
class NormalReturnKind extends ReturnKind, TNormalReturnKind {
int indirectionIndex;
NormalReturnKind() { this = TNormalReturnKind(index) }
NormalReturnKind() { this = TNormalReturnKind(indirectionIndex) }
override int getIndirectionIndex() { result = indirectionIndex }
override string toString() { result = "indirect return" }
}
/**
* A value returned from a callable through a parameter.
*/
private class IndirectReturnKind extends ReturnKind, TIndirectReturnKind {
int argumentIndex;
int indirectionIndex;
IndirectReturnKind() { this = TIndirectReturnKind(argumentIndex, indirectionIndex) }
override int getIndirectionIndex() { result = indirectionIndex }
override string toString() { result = "indirect outparam[" + argumentIndex.toString() + "]" }
}
/** A data flow node that occurs as the result of a `ReturnStmt`. */
class ReturnNode extends Node instanceof IndirectReturnNode {
abstract class ReturnNode extends Node {
/** Gets the kind of this returned value. */
abstract ReturnKind getKind();
}
@@ -510,6 +560,17 @@ class ReturnIndirectionNode extends IndirectReturnNode, ReturnNode {
}
}
/**
* A return node that is part of a summary.
*/
private class SummaryReturnNode extends ReturnNode, FlowSummaryNode {
private ReturnKind rk;
SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), rk) }
override ReturnKind getKind() { result = rk }
}
private Operand fullyConvertedCallStepImpl(Operand op) {
not exists(getANonConversionUse(op)) and
exists(Instruction instr |
@@ -616,7 +677,10 @@ predicate simpleOutNode(Node node, CallInstruction call) {
instructionForFullyConvertedCall(node.asInstruction(), call)
}
/** A data flow node that represents the output of a call. */
/**
* A data flow node that represents the output of a call (for example, a
* return value) at the call site.
*/
class OutNode extends Node {
OutNode() {
// Return values not hidden behind indirections
@@ -627,11 +691,15 @@ class OutNode extends Node {
or
// Modified arguments hidden behind indirections
this instanceof IndirectArgumentOutNode
or
// Summary node
FlowSummaryImpl::Private::summaryOutNode(_, this.(FlowSummaryNode).getSummaryNode(), _)
}
/** Gets the underlying call. */
abstract DataFlowCall getCall();
/** Gets the kind of this out node. */
abstract ReturnKind getReturnKind();
}
@@ -640,25 +708,44 @@ private class DirectCallOutNode extends OutNode {
DirectCallOutNode() { simpleOutNode(this, call) }
override DataFlowCall getCall() { result = call }
override DataFlowCall getCall() { result.asCallInstruction() = call }
override ReturnKind getReturnKind() { result = TNormalReturnKind(0) }
}
private class IndirectCallOutNode extends OutNode, IndirectReturnOutNode {
override DataFlowCall getCall() { result = this.getCallInstruction() }
override DataFlowCall getCall() { result.asCallInstruction() = this.getCallInstruction() }
override ReturnKind getReturnKind() { result = TNormalReturnKind(this.getIndirectionIndex()) }
}
private class SideEffectOutNode extends OutNode, IndirectArgumentOutNode {
override DataFlowCall getCall() { result = this.getCallInstruction() }
override DataFlowCall getCall() { result.asCallInstruction() = this.getCallInstruction() }
override ReturnKind getReturnKind() {
result = TIndirectReturnKind(this.getArgumentIndex(), this.getIndirectionIndex())
}
}
/**
* An output node that is part of a summary. An output node is needed when the
* model contains a synthesized call (`SummaryCall`) and the return value of
* that call is needed by the summary (for example when the model has flow from
* `Argument[0].ReturnValue`).
*/
private class SummaryOutNode extends OutNode, FlowSummaryNode {
private SummaryCall call;
private ReturnKind kind_;
SummaryOutNode() {
FlowSummaryImpl::Private::summaryOutNode(call.getReceiver(), this.getSummaryNode(), kind_)
}
override DataFlowCall getCall() { result = call }
override ReturnKind getReturnKind() { result = kind_ }
}
/**
* Gets a node that can read the value returned from `call` with return kind
* `kind`.
@@ -721,6 +808,10 @@ predicate jumpStep(Node n1, Node n2) {
v = n1.asIndirectVariable(globalDef.getIndirection())
)
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(),
n2.(FlowSummaryNode).getSummaryNode())
}
/**
@@ -729,25 +820,35 @@ predicate jumpStep(Node n1, Node n2) {
* value of `node1`.
*
* The boolean `certain` is true if the destination address does not involve
* any pointer arithmetic, and false otherwise.
* any pointer arithmetic, and false otherwise. This has to do with whether a
* store step can be used to clear a field (see `clearsContent`).
*/
predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolean certain) {
exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store |
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
exists(
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
StoreInstruction store
|
postFieldUpdate = node2 and
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
node2.getIndirectionIndex() = 1 and
numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(),
numberOfLoads, certain)
postFieldUpdate.getIndirectionIndex() = 1 and
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
store.getDestinationAddressOperand(), numberOfLoads, certain)
|
exists(FieldContent fc | fc = c |
fc.getField() = node2.getUpdatedField() and
fc.getField() = postFieldUpdate.getUpdatedField() and
fc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
)
or
exists(UnionContent uc | uc = c |
uc.getAField() = node2.getUpdatedField() and
uc.getAField() = postFieldUpdate.getUpdatedField() and
uc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
)
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode()) and
certain = true
}
/**
@@ -834,6 +935,10 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
uc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
)
)
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
}
/**
@@ -907,21 +1012,213 @@ class CastNode extends Node {
CastNode() { none() } // stub implementation
}
cached
private newtype TDataFlowCallable =
TSourceCallable(Cpp::Declaration decl) {
not decl instanceof FlowSummaryImpl::Public::SummarizedCallable
} or
TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c)
/**
* A function that may contain code or a variable that may contain itself. When
* flow crosses from one _enclosing callable_ to another, the interprocedural
* data-flow library discards call contexts and inserts a node in the big-step
* relation used for human-readable path explanations.
* A callable, which may be:
* - a function (that may contain code)
* - a summarized function (that may contain only `FlowSummaryNode`s)
* - a variable (this is used as context for global initialization, and also
* for the mid-point in interprocedural data flow between a write and read
* of a global variable in different functions).
* When flow crosses from one _enclosing callable_ to another, the
* interprocedural data-flow library discards call contexts and inserts a node
* in the big-step relation used for human-readable path explanations.
*/
class DataFlowCallable = Cpp::Declaration;
class DataFlowCallable extends TDataFlowCallable {
/** Gets the location of this callable. */
Location getLocation() { none() }
/** Gets a textual representation of this callable. */
string toString() { none() }
/**
* Gets the `Declaration` corresponding to this callable if it exists in the database.
* For summarized callables (which may not exist in the database), use `asSummarizedCallable`.
*/
Cpp::Declaration asSourceCallable() { this = TSourceCallable(result) }
/**
* Gets the underlying summarized callable, if
* this callable is generated from a models-as-data
* model.
*/
FlowSummaryImpl::Public::SummarizedCallable asSummarizedCallable() {
this = TSummarizedCallable(result)
}
/**
* Gets the underlying `Declaration` of this `DataFlowCallable`. This
* predicate returns a result for both source and summarized callables.
*/
Cpp::Declaration getUnderlyingCallable() {
result = this.asSummarizedCallable() or // SummarizedCallable = Function (in CPP)
result = this.asSourceCallable()
}
}
/**
* A source callable, conceptually, a function in the source code for the
* purpose of computing data flow. In practice this excludes functions that
* are summarized using models-as-data (as we don't want to create
* unmodeled flows or duplicate paths), and includes variables (for reasons
* explained in `DataFlowCallable`).
*/
class SourceCallable extends DataFlowCallable, TSourceCallable {
Cpp::Declaration decl;
SourceCallable() { this = TSourceCallable(decl) }
override string toString() { result = decl.toString() }
override Location getLocation() { result = decl.getLocation() }
}
/**
* A summarized callable, that is, a function synthesized from one or more
* models-as-data models as a place to contain the corresponding
* `FlowSummaryNode`s.
*/
class SummarizedCallable extends DataFlowCallable, TSummarizedCallable {
FlowSummaryImpl::Public::SummarizedCallable sc;
SummarizedCallable() { this = TSummarizedCallable(sc) }
override string toString() { result = sc.toString() }
override Location getLocation() { result = sc.getLocation() }
}
class DataFlowExpr = Expr;
class DataFlowType = Type;
/** A function call relevant for data flow. */
class DataFlowCall extends CallInstruction {
DataFlowCallable getEnclosingCallable() { result = this.getEnclosingFunction() }
cached
private newtype TDataFlowCall =
TNormalCall(CallInstruction call) or
TSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
/**
* A function call relevant for data flow. This includes calls from source
* code and calls inside library callables with a flow summary.
*/
class DataFlowCall extends TDataFlowCall {
/**
* Gets the underlying data flow call instruction, if any.
*/
CallInstruction asCallInstruction() { none() }
/**
* Gets the operand the specifies the target function of the call.
*/
CallTargetOperand getCallTargetOperand() { none() }
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
DataFlowCallable getStaticCallTarget() { none() }
/**
* Gets the `index`'th argument operand. The qualifier is considered to have index `-1`.
*/
ArgumentOperand getArgumentOperand(int index) { none() }
/**
* Gets the argument at the specified index, or `this` if `index` is `-1`.
*/
pragma[noinline]
final Instruction getArgument(int index) { result = this.getArgumentOperand(index).getDef() }
/**
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/
final int getNumberOfArguments() { result = count(this.getArgumentOperand(_)) }
/**
* Gets the enclosing callable, if any.
*/
DataFlowCallable getEnclosingCallable() { none() }
/**
* Gets a textual representation of this call.
*/
string toString() { none() }
/**
* Gets the location of this call.
*/
Location getLocation() { none() }
}
/**
* A function call relevant for data flow, that exists in source code.
*/
private class NormalCall extends DataFlowCall, TNormalCall {
private CallInstruction call;
NormalCall() { this = TNormalCall(call) }
override CallInstruction asCallInstruction() { result = call }
override CallTargetOperand getCallTargetOperand() { result = call.getCallTargetOperand() }
override DataFlowCallable getStaticCallTarget() {
result.getUnderlyingCallable() = call.getStaticCallTarget()
}
override ArgumentOperand getArgumentOperand(int index) { result = call.getArgumentOperand(index) }
override DataFlowCallable getEnclosingCallable() {
result.getUnderlyingCallable() = call.getEnclosingFunction()
}
override string toString() { result = call.toString() }
override Location getLocation() { result = call.getLocation() }
}
/**
* A synthesized call inside a callable with a flow summary.
*
* For example, consider the function:
* ```
* int myFunction(int (*funPtr)());
* ```
* with an accompanying models-as-data flow summary involving `funPtr` (for
* example from `Argument[0].ReturnValue` to `ReturnValue`). A `SummaryCall`
* will be synthesized representing a call to `funPtr` inside `myFunction`,
* so that flow can be connected as described in the model.
*/
class SummaryCall extends DataFlowCall, TSummaryCall {
private FlowSummaryImpl::Public::SummarizedCallable c;
private FlowSummaryImpl::Private::SummaryNode receiver;
SummaryCall() { this = TSummaryCall(c, receiver) }
/**
* Gets the data flow node that holds the address of the function this call
* targets.
*/
FlowSummaryImpl::Private::SummaryNode getReceiver() { result = receiver }
// no implementation for `getCallTargetOperand()`, `getStaticCallTarget()`
// or `getArgumentOperand(int index)`. This is because the flow summary
// library is responsible for finding the call target, and there are no
// IR nodes available for the call target operand or argument operands.
override DataFlowCallable getEnclosingCallable() { result = TSummarizedCallable(c) }
override string toString() { result = "[summary] call to " + receiver + " in " + c }
override UnknownLocation getLocation() { any() }
}
module IsUnreachableInCall {
@@ -1019,10 +1316,16 @@ predicate neverSkipInPathGraph(Node n) {
class LambdaCallKind = Unit;
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
creation.asInstruction().(FunctionAddressInstruction).getFunctionSymbol() = c.asSourceCallable() and
exists(kind)
}
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
call.(SummaryCall).getReceiver() = receiver.(FlowSummaryNode).getSummaryNode() and
exists(kind)
}
/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
@@ -1038,7 +1341,15 @@ predicate knownSinkModel(Node sink, string model) { none() }
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
* by default as a heuristic.
*/
predicate allowParameterReturnInSelf(ParameterNode p) { p instanceof IndirectParameterNode }
predicate allowParameterReturnInSelf(ParameterNode p) {
p instanceof IndirectParameterNode
or
// models-as-data summarized flow
exists(DataFlowCallable c, ParameterPosition pos |
p.isParameterOf(c, pos) and
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asSummarizedCallable(), pos)
)
}
private predicate fieldHasApproxName(Field f, string s) {
s = f.getName().charAt(0) and

View File

@@ -10,6 +10,7 @@ private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import DataFlowPrivate
private import ModelUtil
private import SsaInternals as Ssa
@@ -59,7 +60,8 @@ private newtype TIRDataFlowNode =
)
} or
TFinalGlobalValue(Ssa::GlobalUse globalUse) or
TInitialGlobalValue(Ssa::GlobalDef globalUse)
TInitialGlobalValue(Ssa::GlobalDef globalUse) or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn)
/**
* An operand that is defined by a `FieldAddressInstruction`.
@@ -735,6 +737,36 @@ class InitialGlobalValue extends Node, TInitialGlobalValue {
override string toStringImpl() { result = globalDef.toString() }
}
/**
* A data-flow node used to model flow summaries. That is, a dataflow node
* that is synthesized to represent a parameter, return value, or other part
* of a models-as-data modeled function.
*/
class FlowSummaryNode extends Node, TFlowSummaryNode {
/**
* Gets the models-as-data `SummaryNode` associated with this dataflow
* `FlowSummaryNode`.
*/
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
/**
* Gets the summarized callable that this node belongs to.
*/
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
result = this.getSummaryNode().getSummarizedCallable()
}
/**
* Gets the enclosing callable. For a `FlowSummaryNode` this is always the
* summarized function this node is part of.
*/
override Declaration getEnclosingCallable() { result = this.getSummarizedCallable() }
override Location getLocationImpl() { result = this.getSummarizedCallable().getLocation() }
override string toStringImpl() { result = this.getSummaryNode().toString() }
}
/**
* INTERNAL: do not use.
*
@@ -826,6 +858,9 @@ class IndirectArgumentOutNode extends PostUpdateNodeImpl {
CallInstruction getCallInstruction() { result.getAnArgumentOperand() = operand }
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
Function getStaticCallTarget() { result = this.getCallInstruction().getStaticCallTarget() }
override string toStringImpl() {
@@ -1635,6 +1670,8 @@ class ParameterNode extends Node {
this.asInstruction() instanceof InitializeParameterInstruction
or
this instanceof IndirectParameterNode
or
FlowSummaryImpl::Private::summaryParameterNode(this.(FlowSummaryNode).getSummaryNode(), _)
}
/**
@@ -1642,7 +1679,7 @@ class ParameterNode extends Node {
* implicit `this` parameter is considered to have position `-1`, and
* pointer-indirection parameters are at further negative positions.
*/
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) { none() } // overridden by subclasses
/** Gets the `Parameter` associated with this node, if it exists. */
Parameter getParameter() { none() } // overridden by subclasses
@@ -1664,8 +1701,9 @@ class DirectParameterNode extends InstructionNode {
private class ExplicitParameterNode extends ParameterNode, DirectParameterNode {
ExplicitParameterNode() { exists(instr.getParameter()) }
override predicate isParameterOf(Function f, ParameterPosition pos) {
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
f.getUnderlyingCallable().(Function).getParameter(pos.(DirectPosition).getIndex()) =
instr.getParameter()
}
override string toStringImpl() { result = instr.getParameter().toString() }
@@ -1677,13 +1715,32 @@ private class ExplicitParameterNode extends ParameterNode, DirectParameterNode {
class ThisParameterNode extends ParameterNode, DirectParameterNode {
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
override predicate isParameterOf(Function f, ParameterPosition pos) {
pos.(DirectPosition).getIndex() = -1 and instr.getEnclosingFunction() = f
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
pos.(DirectPosition).getIndex() = -1 and
instr.getEnclosingFunction() = f.getUnderlyingCallable()
}
override string toStringImpl() { result = "this" }
}
/**
* A parameter node that is part of a summary.
*/
class SummaryParameterNode extends ParameterNode, FlowSummaryNode {
SummaryParameterNode() {
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), _)
}
private ParameterPosition getPosition() {
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), result)
}
override predicate isParameterOf(DataFlowCallable c, ParameterPosition p) {
c.getUnderlyingCallable() = this.getSummarizedCallable() and
p = this.getPosition()
}
}
pragma[noinline]
private predicate indirectPositionHasArgumentIndexAndIndex(
IndirectionPosition pos, int argumentIndex, int indirectionIndex
@@ -1702,8 +1759,8 @@ private predicate indirectParameterNodeHasArgumentIndexAndIndex(
/** A synthetic parameter to model the pointed-to object of a pointer parameter. */
class ParameterIndirectionNode extends ParameterNode instanceof IndirectParameterNode {
override predicate isParameterOf(Function f, ParameterPosition pos) {
IndirectParameterNode.super.getEnclosingCallable() = f and
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
IndirectParameterNode.super.getEnclosingCallable() = f.getUnderlyingCallable() and
exists(int argumentIndex, int indirectionIndex |
indirectPositionHasArgumentIndexAndIndex(pos, argumentIndex, indirectionIndex) and
indirectParameterNodeHasArgumentIndexAndIndex(this, argumentIndex, indirectionIndex)
@@ -1753,6 +1810,22 @@ abstract private class PartialDefinitionNode extends PostUpdateNode {
abstract Expr getDefinedExpr();
}
/**
* A `PostUpdateNode` that is part of a flow summary. These are synthesized,
* for example, when a models-as-data summary models a write to a field since
* the write needs to target a `PostUpdateNode`.
*/
class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
SummaryPostUpdateNode() {
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(), _)
}
override Node getPreUpdateNode() {
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(),
result.(FlowSummaryNode).getSummaryNode())
}
}
/**
* A node that represents the value of a variable after a function call that
* may have changed the variable because it's passed by reference.
@@ -1889,10 +1962,20 @@ cached
private module Cached {
/**
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
* (intra-procedural) step. This relation is only used for local dataflow
* (for example `DataFlow::localFlow(source, sink)`) so it contains
* special cases that should only apply to local dataflow.
*/
cached
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo, _) }
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
// common dataflow steps
simpleLocalFlowStep(nodeFrom, nodeTo, _)
or
// models-as-data summarized flow for local data flow (i.e. special case for flow
// through calls to modeled functions, without relying on global dataflow to join
// the dots).
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
}
private predicate indirectionOperandFlow(RawIndirectOperand nodeFrom, Node nodeTo) {
nodeFrom != nodeTo and
@@ -1958,8 +2041,9 @@ private module Cached {
/**
* INTERNAL: do not use.
*
* This is the local flow predicate that's used as a building block in global
* data flow. It may have less flow than the `localFlowStep` predicate.
* This is the local flow predicate that's used as a building block in both
* local and global data flow. It may have less flow than the `localFlowStep`
* predicate.
*/
cached
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
@@ -2001,6 +2085,10 @@ private module Cached {
// function such as `operator[]`.
reverseFlow(nodeFrom, nodeTo) and
model = ""
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
}
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
@@ -2242,6 +2330,8 @@ private Field getAFieldWithSize(Union u, int bytes) {
cached
private newtype TContent =
TFieldContent(Field f, int indirectionIndex) {
// the indirection index for field content starts at 1 (because `TFieldContent` is thought of as
// the address of the field, `FieldAddress` in the IR).
indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(f.getUnspecifiedType())] and
// Reads and writes of union fields are tracked using `UnionContent`.
not f.getDeclaringType() instanceof Union
@@ -2251,7 +2341,8 @@ private newtype TContent =
f = u.getAField() and
bytes = getFieldSize(f) and
// We key `UnionContent` by the union instead of its fields since a write to one
// field can be read by any read of the union's fields.
// field can be read by any read of the union's fields. Again, the indirection index
// is 1-based (because 0 is considered the address).
indirectionIndex =
[1 .. max(Ssa::getMaxIndirectionsForType(getAFieldWithSize(u, bytes).getUnspecifiedType()))]
)
@@ -2285,16 +2376,14 @@ class Content extends TContent {
abstract predicate impliesClearOf(Content c);
}
/**
* Gets a string consisting of `n` star characters ("*"), where n >= 0. This is
* used to represent indirection.
*/
bindingset[n]
string repeatStars(int n) { result = concat(int i | i in [1 .. n] | "*") }
private module ContentStars {
private int maxNumberOfIndirections() { result = max(any(Content c).getIndirectionIndex()) }
private string repeatStars(int n) {
n = 0 and result = ""
or
n = [1 .. maxNumberOfIndirections()] and
result = "*" + repeatStars(n - 1)
}
/**
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
* output for `c`.
@@ -2374,6 +2463,12 @@ class UnionContent extends Content, TUnionContent {
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
*/
class ContentSet instanceof Content {
/**
* Holds if this content set is the singleton `{c}`. At present, this is
* the only kind of content set supported in C/C++.
*/
predicate isSingleton(Content c) { this = c }
/** Gets a content that may be stored into when storing into this set. */
Content getAStoreContent() { result = this }
@@ -2591,5 +2686,5 @@ class AdditionalCallTarget extends Unit {
/**
* Gets a viable target for `call`.
*/
abstract DataFlowCallable viableTarget(Call call);
abstract Declaration viableTarget(Call call);
}

View File

@@ -19,15 +19,6 @@ private module SourceVariables {
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
}
private int maxNumberOfIndirections() { result = max(SourceVariable sv | | sv.getIndirection()) }
private string repeatStars(int n) {
n = 0 and result = ""
or
n = [1 .. maxNumberOfIndirections()] and
result = "*" + repeatStars(n - 1)
}
class SourceVariable extends TSourceVariable {
BaseSourceVariable base;
int ind;
@@ -74,19 +65,28 @@ private module SourceVariables {
import SourceVariables
/**
* Holds if the `(operand, indirectionIndex)` columns should be
* assigned a `RawIndirectOperand` value.
* Holds if `indirectionIndex` is a valid non-zero indirection index for
* operand `op`. That is, `indirectionIndex` is between 1 and the maximum
* indirection for the operand's type.
*/
predicate hasRawIndirectOperand(Operand op, int indirectionIndex) {
predicate hasIndirectOperand(Operand op, int indirectionIndex) {
exists(CppType type, int m |
not ignoreOperand(op) and
type = getLanguageType(op) and
m = countIndirectionsForCppType(type) and
indirectionIndex = [1 .. m] and
not hasIRRepresentationOfIndirectOperand(op, indirectionIndex, _, _)
indirectionIndex = [1 .. m]
)
}
/**
* Holds if the `(operand, indirectionIndex)` columns should be
* assigned a `RawIndirectOperand` value.
*/
predicate hasRawIndirectOperand(Operand op, int indirectionIndex) {
hasIndirectOperand(op, indirectionIndex) and
not hasIRRepresentationOfIndirectOperand(op, indirectionIndex, _, _)
}
/**
* Holds if the `(instr, indirectionIndex)` columns should be
* assigned a `RawIndirectInstruction` value.
@@ -443,6 +443,8 @@ class FinalParameterUse extends UseImpl, TFinalParameterUse {
Parameter getParameter() { result = p }
int getArgumentIndex() { result = p.getIndex() }
override Node getNode() { finalParameterNodeHasParameterAndIndex(result, p, ind) }
override int getIndirection() { result = ind + 1 }
@@ -891,7 +893,7 @@ private predicate isArgumentOfCallableInstruction(DataFlowCall call, Instruction
}
private predicate isArgumentOfCallableOperand(DataFlowCall call, Operand operand) {
operand.(ArgumentOperand).getCall() = call
operand = call.getArgumentOperand(_)
or
exists(FieldAddressInstruction fai |
fai.getObjectAddressOperand() = operand and

View File

@@ -6,16 +6,26 @@ private import semmle.code.cpp.models.interfaces.SideEffect
private import DataFlowUtil
private import DataFlowPrivate
private import SsaInternals as Ssa
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.cpp.ir.dataflow.FlowSteps
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
* (intra-procedural) step. This relation is only used for local taint flow
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
* special cases that should only apply to local taint flow.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// dataflow step
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
// taint flow step
localAdditionalTaintStep(nodeFrom, nodeTo, _)
or
// models-as-data summarized flow for local data flow (i.e. special case for flow
// through calls to modeled functions, without relying on global dataflow to join
// the dots).
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
}
/**
@@ -42,6 +52,10 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
model = ""
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
or
// object->field conflation for content that is a `TaintInheritingContent`.
exists(DataFlow::ContentSet f |
readStep(nodeFrom, f, nodeTo) and

View File

@@ -1,5 +1,6 @@
private import implementations.Allocation
private import implementations.Deallocation
private import implementations.Fopen
private import implementations.Fread
private import implementations.Getenv
private import implementations.Gets
@@ -41,4 +42,4 @@ private import implementations.SqLite3
private import implementations.PostgreSql
private import implementations.System
private import implementations.StructuredExceptionHandling
private import implementations.Fopen
private import implementations.ZMQ

View File

@@ -112,3 +112,21 @@ private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunctio
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
}
/**
* A model for `getc` and similar functions that are flow sources.
*/
private class GetcSource extends SourceModelCsv {
override predicate row(string row) {
row =
[
";;false;getc;;;ReturnValue;remote", ";;false;getwc;;;ReturnValue;remote",
";;false;_getc_nolock;;;ReturnValue;remote", ";;false;_getwc_nolock;;;ReturnValue;remote",
";;false;getch;;;ReturnValue;local", ";;false;_getch;;;ReturnValue;local",
";;false;_getwch;;;ReturnValue;local", ";;false;_getch_nolock;;;ReturnValue;local",
";;false;_getwch_nolock;;;ReturnValue;local", ";;false;getchar;;;ReturnValue;local",
";;false;getwchar;;;ReturnValue;local", ";;false;_getchar_nolock;;;ReturnValue;local",
";;false;_getwchar_nolock;;;ReturnValue;local",
]
}
}

View File

@@ -0,0 +1,45 @@
/**
* Provides implementation classes modeling the ZeroMQ networking library.
*/
import semmle.code.cpp.models.interfaces.FlowSource
/**
* Remote flow sources.
*/
private class ZmqSource extends SourceModelCsv {
override predicate row(string row) {
row =
[
";;false;zmq_recv;;;Argument[*1];remote", ";;false;zmq_recvmsg;;;Argument[*1];remote",
";;false;zmq_msg_recv;;;Argument[*0];remote",
]
}
}
/**
* Remote flow sinks.
*/
private class ZmqSinks extends SinkModelCsv {
override predicate row(string row) {
row =
[
";;false;zmq_send;;;Argument[*1];remote-sink",
";;false;zmq_sendmsg;;;Argument[*1];remote-sink",
";;false;zmq_msg_send;;;Argument[*0];remote-sink",
]
}
}
/**
* Flow steps.
*/
private class ZmqSummaries extends SummaryModelCsv {
override predicate row(string row) {
row =
[
";;false;zmq_msg_init_data;;;Argument[*1];Argument[*0];taint",
";;false;zmq_msg_data;;;Argument[*0];ReturnValue[*];taint",
]
}
}

View File

@@ -9,6 +9,7 @@
import cpp
import FunctionInputsAndOutputs
import semmle.code.cpp.models.Models
import semmle.code.cpp.dataflow.ExternalFlow
/**
* A library function that returns data that may be read from a network connection.

View File

@@ -20,6 +20,9 @@ abstract class RemoteFlowSource extends FlowSource { }
/** A data flow source of local user input. */
abstract class LocalFlowSource extends FlowSource { }
/**
* A remote data flow source that is defined through a `RemoteFlowSourceFunction` model.
*/
private class RemoteModelSource extends RemoteFlowSource {
string sourceType;
@@ -34,6 +37,9 @@ private class RemoteModelSource extends RemoteFlowSource {
override string getSourceType() { result = sourceType }
}
/**
* A local data flow source that is defined through a `LocalFlowSourceFunction` model.
*/
private class LocalModelSource extends LocalFlowSource {
string sourceType;
@@ -48,6 +54,9 @@ private class LocalModelSource extends LocalFlowSource {
override string getSourceType() { result = sourceType }
}
/**
* A local data flow source that the `argv` parameter to `main`.
*/
private class ArgvSource extends LocalFlowSource {
ArgvSource() {
exists(Function main, Parameter argv |
@@ -60,12 +69,33 @@ private class ArgvSource extends LocalFlowSource {
override string getSourceType() { result = "a command-line argument" }
}
/**
* A remote data flow source that is defined through 'models as data'.
*/
private class ExternalRemoteFlowSource extends RemoteFlowSource {
ExternalRemoteFlowSource() { sourceNode(this, "remote") }
override string getSourceType() { result = "external" }
}
/**
* A local data flow source that is defined through 'models as data'.
*/
private class ExternalLocalFlowSource extends LocalFlowSource {
ExternalLocalFlowSource() { sourceNode(this, "local") }
override string getSourceType() { result = "external" }
}
/** A remote data flow sink. */
abstract class RemoteFlowSink extends DataFlow::Node {
/** Gets a string that describes the type of this flow sink. */
abstract string getSinkType();
}
/**
* A remote flow sink derived from the `RemoteFlowSinkFunction` model.
*/
private class RemoteParameterSink extends RemoteFlowSink {
string sourceType;
@@ -79,3 +109,12 @@ private class RemoteParameterSink extends RemoteFlowSink {
override string getSinkType() { result = sourceType }
}
/**
* A remote flow sink defined in a CSV model.
*/
private class RemoteFlowFromCsvSink extends RemoteFlowSink {
RemoteFlowFromCsvSink() { sinkNode(this, "remote-sink") }
override string getSinkType() { result = "remote flow sink" }
}

View File

@@ -139,6 +139,7 @@ private module ParameterSinks {
}
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
/**
* Holds if `n` represents the expression `e`, and `e` is a pointer that is
@@ -149,11 +150,11 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
predicate isUse(DataFlow::Node n, Expr e) {
isUse0(e) and n.asExpr() = e
or
exists(CallInstruction call, InitializeParameterInstruction init |
exists(DataFlowCall call, InitializeParameterInstruction init |
n.asOperand().getDef().getUnconvertedResultExpression() = e and
pragma[only_bind_into](init) = ParameterSinks::getAnAlwaysDereferencedParameter() and
viableParamArg(call, DataFlow::instructionNode(init), n) and
pragma[only_bind_out](init.getEnclosingFunction()) =
pragma[only_bind_out](call.getStaticCallTarget())
pragma[only_bind_out](call.asCallInstruction().getStaticCallTarget())
)
}