From 2dc2607ef3b3817bf75e18a06ff3dc0e330a8fcb Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 24 Feb 2026 22:14:49 +0000 Subject: [PATCH 1/4] TODO --- .../ir/dataflow/internal/DataFlowNodes.qll | 1815 ++++++++++++++++ .../ir/dataflow/internal/DataFlowPrivate.qll | 385 ++-- .../cpp/ir/dataflow/internal/DataFlowUtil.qll | 1901 +---------------- .../cpp/ir/dataflow/internal/ExprNodes.qll | 1 + .../cpp/ir/dataflow/internal/ModelUtil.qll | 1 + .../code/cpp/ir/dataflow/internal/SsaImpl.qll | 1 + .../ir/dataflow/internal/SsaImplCommon.qll | 41 +- .../dataflow/internal/TaintTrackingUtil.qll | 118 +- .../Memory Management/AllocaInLoop.ql | 7 +- .../modelgenerator/internal/CaptureModels.qll | 5 +- .../models-as-data/FlowSummaryNode.ql | 1 + 11 files changed, 2126 insertions(+), 2150 deletions(-) create mode 100644 cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll new file mode 100644 index 000000000000..fc94d3bfa750 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll @@ -0,0 +1,1815 @@ +/** + * Provides node classes for the C++ IR data flow graph. + */ + +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.models.interfaces.DataFlow +private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl +private import DataFlowPrivate +private import DataFlowUtil +private import ModelUtil +private import SsaImpl as SsaImpl +private import DataFlowImplCommon as DataFlowImplCommon +private import codeql.util.Unit +private import Node0ToString +import ExprNodes + +/** + * A canonical representation of a field. + * + * For performance reasons we want a unique `Content` that represents + * a given field across any template instantiation of a class. + * + * This is possible in _almost_ all cases, but there are cases where it is + * not possible to map between a field in the uninstantiated template to a + * field in the instantiated template. This happens in the case of local class + * definitions (because the local class is not the template that constructs + * the instantiation - it is the enclosing function). So this abstract class + * has two implementations: a non-local case (where we can represent a + * canonical field as the field declaration from an uninstantiated class + * template or a non-templated class), and a local case (where we simply use + * the field from the instantiated class). + */ +abstract class CanonicalField extends Field { + /** Gets a field represented by this canonical field. */ + abstract Field getAField(); + + /** + * Gets a class that declares a field represented by this canonical field. + */ + abstract Class getADeclaringType(); + + /** + * Gets a type that this canonical field may have. Note that this may + * not be a unique type. For example, consider this case: + * ``` + * template + * struct S { T x; }; + * + * S s1; + * S s2; + * ``` + * In this case the canonical field corresponding to `S::x` has two types: + * `int` and `char`. + */ + Type getAType() { result = this.getAField().getType() } + + Type getAnUnspecifiedType() { result = this.getAType().getUnspecifiedType() } +} + +private class NonLocalCanonicalField extends CanonicalField { + Class declaringType; + + NonLocalCanonicalField() { + declaringType = this.getDeclaringType() and + not declaringType.isFromTemplateInstantiation(_) and + not declaringType.isLocal() // handled in LocalCanonicalField + } + + override Field getAField() { + exists(Class c | result.getDeclaringType() = c | + // Either the declaring class of the field is a template instantiation + // that has been constructed from this canonical declaration + c.isConstructedFrom(declaringType) and + pragma[only_bind_out](result.getName()) = pragma[only_bind_out](this.getName()) + or + // or this canonical declaration is not a template. + not c.isConstructedFrom(_) and + result = this + ) + } + + override Class getADeclaringType() { + result = this.getDeclaringType() + or + result.isConstructedFrom(this.getDeclaringType()) + } +} + +private class LocalCanonicalField extends CanonicalField { + Class declaringType; + + LocalCanonicalField() { + declaringType = this.getDeclaringType() and + declaringType.isLocal() + } + + override Field getAField() { result = this } + + override Class getADeclaringType() { result = declaringType } +} + +/** + * A canonical representation of a `Union`. See `CanonicalField` for the explanation for + * why we need a canonical representation. + */ +abstract class CanonicalUnion extends Union { + /** Gets a union represented by this canonical union. */ + abstract Union getAUnion(); + + /** Gets a canonical field of this canonical union. */ + CanonicalField getACanonicalField() { result.getDeclaringType() = this } +} + +private class NonLocalCanonicalUnion extends CanonicalUnion { + NonLocalCanonicalUnion() { not this.isFromTemplateInstantiation(_) and not this.isLocal() } + + override Union getAUnion() { + result = this + or + result.isConstructedFrom(this) + } +} + +private class LocalCanonicalUnion extends CanonicalUnion { + LocalCanonicalUnion() { this.isLocal() } + + override Union getAUnion() { result = this } +} + +bindingset[f] +pragma[inline_late] +int getFieldSize(CanonicalField f) { result = max(f.getAType().getSize()) } + +/** + * Gets a field in the union `u` whose size + * is `bytes` number of bytes. + */ +private CanonicalField getAFieldWithSize(CanonicalUnion u, int bytes) { + result = u.getACanonicalField() and + bytes = getFieldSize(result) +} + +cached +private module Cached { + cached + newtype TContent = + TNonUnionContent(CanonicalField f, int indirectionIndex) { + // the indirection index for field content starts at 1 (because `TNonUnionContent` is thought of as + // the address of the field, `FieldAddress` in the IR). + indirectionIndex = [1 .. max(SsaImpl::getMaxIndirectionsForType(f.getAnUnspecifiedType()))] and + // Reads and writes of union fields are tracked using `UnionContent`. + not f.getDeclaringType() instanceof Union + } or + TUnionContent(CanonicalUnion u, int bytes, int indirectionIndex) { + exists(CanonicalField f | + f = u.getACanonicalField() and + bytes = getFieldSize(f) and + // We key `UnionContent` by the union instead of its fields since a write to one + // field can be read by any read of the union's fields. Again, the indirection index + // is 1-based (because 0 is considered the address). + indirectionIndex = + [1 .. max(SsaImpl::getMaxIndirectionsForType(getAFieldWithSize(u, bytes) + .getAnUnspecifiedType()) + )] + ) + } or + TElementContent(int indirectionIndex) { + indirectionIndex = [1 .. getMaxElementContentIndirectionIndex()] + } + + /** + * The IR dataflow graph consists of the following nodes: + * - `Node0`, which injects most instructions and operands directly into the + * dataflow graph. + * - `VariableNode`, which is used to model flow through global variables. + * - `PostUpdateNodeImpl`, which is used to model the state of an object after + * an update after a number of loads. + * - `SsaSynthNode`, which represents synthesized nodes as computed by the shared SSA + * library. + * - `RawIndirectOperand`, which represents the value of `operand` after + * loading the address a number of times. + * - `RawIndirectInstruction`, which represents the value of `instr` after + * loading the address a number of times. + */ + cached + newtype TIRDataFlowNode = + TNode0(Node0Impl node) { DataFlowImplCommon::forceCachingInSameStage() } or + TGlobalLikeVariableNode(GlobalLikeVariable var, int indirectionIndex) { + indirectionIndex = + [getMinIndirectionsForType(var.getUnspecifiedType()) .. SsaImpl::getMaxIndirectionsForType(var.getUnspecifiedType())] + } or + TPostUpdateNodeImpl(Operand operand, int indirectionIndex) { + isPostUpdateNodeImpl(operand, indirectionIndex) + } or + TSsaSynthNode(SsaImpl::SynthNode n) or + TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or + TRawIndirectOperand0(Node0Impl node, int indirectionIndex) { + SsaImpl::hasRawIndirectOperand(node.asOperand(), indirectionIndex) + } or + TRawIndirectInstruction0(Node0Impl node, int indirectionIndex) { + not exists(node.asOperand()) and + SsaImpl::hasRawIndirectInstruction(node.asInstruction(), indirectionIndex) + } or + TFinalParameterNode(Parameter p, int indirectionIndex) { + exists(SsaImpl::FinalParameterUse use | + use.getParameter() = p and + use.getIndirectionIndex() = indirectionIndex + ) + } or + TFinalGlobalValue(SsaImpl::GlobalUse globalUse) or + TInitialGlobalValue(SsaImpl::GlobalDef globalUse) or + TBodyLessParameterNodeImpl(Parameter p, int indirectionIndex) { + // Rule out parameters of catch blocks. + not exists(p.getCatchBlock()) and + // We subtract one because `getMaxIndirectionsForType` returns the maximum + // indirection for a glvalue of a given type, and this doesn't apply to + // parameters. + indirectionIndex = [0 .. SsaImpl::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1] and + not any(InitializeParameterInstruction init).getParameter() = p + } or + TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) +} + +import Cached + +/** + * An operand that is defined by a `FieldAddressInstruction`. + */ +class FieldAddress extends Operand { + FieldAddressInstruction fai; + + FieldAddress() { fai = this.getDef() and not SsaImpl::ignoreOperand(this) } + + /** Gets the field associated with this instruction. */ + Field getField() { result = fai.getField() } + + /** Gets the instruction whose result provides the address of the object containing the field. */ + Instruction getObjectAddress() { result = fai.getObjectAddress() } + + /** Gets the operand that provides the address of the object containing the field. */ + Operand getObjectAddressOperand() { result = fai.getObjectAddressOperand() } +} + +/** + * Holds if `opFrom` is an operand whose value flows to the result of `instrTo`. + * + * `isPointerArith` is `true` if `instrTo` is a `PointerArithmeticInstruction` and `opFrom` + * is the left operand. + * + * `additional` is `true` if the conversion is supplied by an implementation of the + * `Indirection` class. It is sometimes useful to exclude such conversions. + */ +predicate conversionFlow( + Operand opFrom, Instruction instrTo, boolean isPointerArith, boolean additional +) { + isPointerArith = false and + ( + additional = false and + ( + instrTo.(CopyValueInstruction).getSourceValueOperand() = opFrom + or + instrTo.(ConvertInstruction).getUnaryOperand() = opFrom + or + instrTo.(CheckedConvertOrNullInstruction).getUnaryOperand() = opFrom + or + instrTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom + or + exists(BuiltInInstruction builtIn | + builtIn = instrTo and + // __builtin_bit_cast + builtIn.getBuiltInOperation() instanceof BuiltInBitCast and + opFrom = builtIn.getAnOperand() + ) + ) + or + additional = true and + SsaImpl::isAdditionalConversionFlow(opFrom, instrTo) + ) + or + isPointerArith = true and + additional = false and + instrTo.(PointerArithmeticInstruction).getLeftOperand() = opFrom +} + +module Public { + /** + * A node in a data flow graph. + * + * A node can be either an expression, a parameter, or an uninitialized local + * variable. Such nodes are created with `DataFlow::exprNode`, + * `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively. + */ + class Node extends TIRDataFlowNode { + /** + * INTERNAL: Do not use. + */ + DataFlowCallable getEnclosingCallable() { none() } // overridden in subclasses + + /** Gets the function to which this node belongs, if any. */ + Declaration getFunction() { none() } // overridden in subclasses + + /** Holds if this node represents a glvalue. */ + predicate isGLValue() { none() } + + /** + * Gets the type of this node. + * + * If `isGLValue()` holds, then the type of this node + * should be thought of as "pointer to `getType()`". + */ + Type getType() { none() } // overridden in subclasses + + /** Gets the instruction corresponding to this node, if any. */ + Instruction asInstruction() { result = this.(InstructionNode).getInstruction() } + + /** Gets the operands corresponding to this node, if any. */ + Operand asOperand() { result = this.(OperandNode).getOperand() } + + /** + * Gets the operand that is indirectly tracked by this node behind `index` + * number of indirections. + */ + Operand asIndirectOperand(int index) { hasOperandAndIndex(this, result, index) } + + /** + * Holds if this node is at index `i` in basic block `block`. + * + * Note: Phi nodes are considered to be at index `-1`. + */ + final predicate hasIndexInBlock(IRBlock block, int i) { + this.asInstruction() = block.getInstruction(i) + or + this.asOperand().getUse() = block.getInstruction(i) + or + exists(SsaImpl::SynthNode ssaNode | + this.(SsaSynthNode).getSynthNode() = ssaNode and + ssaNode.getBasicBlock() = block and + ssaNode.getIndex() = i + ) + or + this.(RawIndirectOperand).getOperand().getUse() = block.getInstruction(i) + or + this.(RawIndirectInstruction).getInstruction() = block.getInstruction(i) + or + this.(PostUpdateNode).getPreUpdateNode().hasIndexInBlock(block, i) + } + + /** Gets the basic block of this node, if any. */ + final IRBlock getBasicBlock() { this.hasIndexInBlock(result, _) } + + /** + * Gets the non-conversion expression corresponding to this node, if any. + * This predicate only has a result on nodes that represent the value of + * evaluating the expression. For data flowing _out of_ an expression, like + * when an argument is passed by reference, use `asDefiningArgument` instead + * of `asExpr`. + * + * If this node strictly (in the sense of `asConvertedExpr`) corresponds to + * a `Conversion`, then the result is the underlying non-`Conversion` base + * expression. + */ + Expr asExpr() { result = this.asExpr(_) } + + /** + * INTERNAL: Do not use. + */ + Expr asExpr(int n) { result = this.(ExprNode).getExpr(n) } + + /** + * INTERNAL: Do not use. + */ + Expr asIndirectExpr(int n, int index) { result = this.(IndirectExprNode).getExpr(n, index) } + + /** + * Gets the non-conversion expression that's indirectly tracked by this node + * under `index` number of indirections. + */ + Expr asIndirectExpr(int index) { result = this.asIndirectExpr(_, index) } + + /** + * Gets the non-conversion expression that's indirectly tracked by this node + * behind a number of indirections. + */ + Expr asIndirectExpr() { result = this.asIndirectExpr(_) } + + /** + * Gets the expression corresponding to this node, if any. The returned + * expression may be a `Conversion`. + */ + Expr asConvertedExpr() { result = this.asConvertedExpr(_) } + + /** + * Gets the expression corresponding to this node, if any. The returned + * expression may be a `Conversion`. + */ + Expr asConvertedExpr(int n) { result = this.(ExprNode).getConvertedExpr(n) } + + private Expr asIndirectConvertedExpr(int n, int index) { + result = this.(IndirectExprNode).getConvertedExpr(n, index) + } + + /** + * Gets the expression that's indirectly tracked by this node + * behind `index` number of indirections. + */ + Expr asIndirectConvertedExpr(int index) { result = this.asIndirectConvertedExpr(_, index) } + + /** + * Gets the expression that's indirectly tracked by this node behind a + * number of indirections. + */ + Expr asIndirectConvertedExpr() { result = this.asIndirectConvertedExpr(_) } + + /** + * Gets the argument that defines this `DefinitionByReferenceNode`, if any. + * This predicate should be used instead of `asExpr` when referring to the + * value of a reference argument _after_ the call has returned. For example, + * in `f(&x)`, this predicate will have `&x` as its result for the `Node` + * that represents the new value of `x`. + */ + Expr asDefiningArgument() { result = this.asDefiningArgument(_) } + + /** + * Gets the definition associated with this node, if any. + * + * For example, consider the following example + * ```cpp + * int x = 42; // 1 + * x = 34; // 2 + * ++x; // 3 + * x++; // 4 + * x += 1; // 5 + * int y = x += 2; // 6 + * ``` + * - For (1) the result is `42`. + * - For (2) the result is `x = 34`. + * - For (3) the result is `++x`. + * - For (4) the result is `x++`. + * - For (5) the result is `x += 1`. + * - For (6) there are two results: + * - For the definition generated by `x += 2` the result is `x += 2` + * - For the definition generated by `int y = ...` the result is + * also `x += 2`. + * + * For assignments, `node.asDefinition()` and `node.asExpr()` will both exist + * for the same dataflow node. However, for expression such as `x++` that + * both write to `x` and read the current value of `x`, `node.asDefinition()` + * will give the node corresponding to the value after the increment, and + * `node.asExpr()` will give the node corresponding to the value before the + * increment. For an example of this, consider the following: + * + * ```cpp + * sink(x++); + * ``` + * in the above program, there will not be flow from a node `n` such that + * `n.asDefinition() instanceof IncrementOperation` to the argument of `sink` + * since the value passed to `sink` is the value before to the increment. + * However, there will be dataflow from a node `n` such that + * `n.asExpr() instanceof IncrementOperation` since the result of evaluating + * the expression `x++` is passed to `sink`. + */ + Expr asDefinition() { result = this.asDefinition(_) } + + private predicate isCertainStore() { + exists(SsaImpl::Definition def | + SsaImpl::defToNode(this, def, _) and + def.isCertain() + ) + } + + /** + * Gets the definition associated with this node, if any. + * + * For example, consider the following example + * ```cpp + * int x = 42; // 1 + * x = 34; // 2 + * ++x; // 3 + * x++; // 4 + * x += 1; // 5 + * int y = x += 2; // 6 + * ``` + * - For (1) the result is `42`. + * - For (2) the result is `x = 34`. + * - For (3) the result is `++x`. + * - For (4) the result is `x++`. + * - For (5) the result is `x += 1`. + * - For (6) there are two results: + * - For the definition generated by `x += 2` the result is `x += 2` + * - For the definition generated by `int y = ...` the result is + * also `x += 2`. + * + * For assignments, `node.asDefinition(_)` and `node.asExpr()` will both exist + * for the same dataflow node. However, for expression such as `x++` that + * both write to `x` and read the current value of `x`, `node.asDefinition(_)` + * will give the node corresponding to the value after the increment, and + * `node.asExpr()` will give the node corresponding to the value before the + * increment. For an example of this, consider the following: + * + * ```cpp + * sink(x++); + * ``` + * in the above program, there will not be flow from a node `n` such that + * `n.asDefinition(_) instanceof IncrementOperation` to the argument of `sink` + * since the value passed to `sink` is the value before to the increment. + * However, there will be dataflow from a node `n` such that + * `n.asExpr() instanceof IncrementOperation` since the result of evaluating + * the expression `x++` is passed to `sink`. + * + * If `uncertain = false` then the definition is guaranteed to overwrite + * the entire buffer pointed to by the destination address of the definition. + * Otherwise, `uncertain = true`. + * + * For example, the write `int x; x = 42;` is guaranteed to overwrite all the + * bytes allocated to `x`, while the assignment `int p[10]; p[3] = 42;` has + * `uncertain = true` since the write will not overwrite the entire buffer + * pointed to by `p`. + */ + Expr asDefinition(boolean uncertain) { + exists(StoreInstruction store | + store = this.asInstruction() and + result = asDefinitionImpl(store) and + if this.isCertainStore() then uncertain = false else uncertain = true + ) + } + + /** + * Gets the definition associated with this node, if this node is a certain definition. + * + * See `Node.asDefinition/1` for a description of certain and uncertain definitions. + */ + Expr asCertainDefinition() { result = this.asDefinition(false) } + + /** + * Gets the definition associated with this node, if this node is an uncertain definition. + * + * See `Node.asDefinition/1` for a description of certain and uncertain definitions. + */ + Expr asUncertainDefinition() { result = this.asDefinition(true) } + + /** + * Gets the indirect definition at a given indirection corresponding to this + * node, if any. + * + * See the comments on `Node.asDefinition` for examples. + */ + Expr asIndirectDefinition(int indirectionIndex) { + exists(StoreInstruction store | + this.(IndirectInstruction).hasInstructionAndIndirectionIndex(store, indirectionIndex) and + result = asDefinitionImpl(store) + ) + } + + /** + * Gets the indirect definition at some indirection corresponding to this + * node, if any. + */ + Expr asIndirectDefinition() { result = this.asIndirectDefinition(_) } + + /** + * Gets the argument that defines this `DefinitionByReferenceNode`, if any. + * + * Unlike `Node::asDefiningArgument/0`, this predicate gets the node representing + * the value of the `index`'th indirection after leaving a function. For example, + * in: + * ```cpp + * void f(int**); + * ... + * int** x = ...; + * f(x); + * ``` + * The node `n` such that `n.asDefiningArgument(1)` is the argument `x` will + * contain the value of `*x` after `f` has returned, and the node `n` such that + * `n.asDefiningArgument(2)` is the argument `x` will contain the value of `**x` + * after the `f` has returned. + */ + Expr asDefiningArgument(int index) { + this.(DefinitionByReferenceNode).getIndirectionIndex() = index and + result = this.(DefinitionByReferenceNode).getArgument() + } + + /** + * Gets the the argument going into a function for a node that represents + * the indirect value of the argument after `index` loads. For example, in: + * ```cpp + * void f(int**); + * ... + * int** x = ...; + * f(x); + * ``` + * The node `n` such that `n.asIndirectArgument(1)` represents the value of + * `*x` going into `f`, and the node `n` such that `n.asIndirectArgument(2)` + * represents the value of `**x` going into `f`. + */ + Expr asIndirectArgument(int index) { + this.(SideEffectOperandNode).hasAddressOperandAndIndirectionIndex(_, index) and + result = this.(SideEffectOperandNode).getArgument() + } + + /** + * Gets the the argument going into a function for a node that represents + * the indirect value of the argument after any non-zero number of loads. + */ + Expr asIndirectArgument() { result = this.asIndirectArgument(_) } + + /** Gets the positional parameter corresponding to this node, if any. */ + Parameter asParameter() { + exists(int indirectionIndex | result = this.asParameter(indirectionIndex) | + if result.getUnspecifiedType() instanceof ReferenceType + then indirectionIndex = 1 + else indirectionIndex = 0 + ) + } + + /** + * Gets the uninitialized local variable corresponding to this node, if + * any. + */ + LocalVariable asUninitialized() { result = this.(UninitializedNode).getLocalVariable() } + + /** + * Gets the positional parameter corresponding to the node that represents + * the value of the parameter after `index` number of loads, if any. For + * example, in: + * ```cpp + * void f(int** x) { ... } + * ``` + * - The node `n` such that `n.asParameter(0)` is the parameter `x` represents + * the value of `x`. + * - The node `n` such that `n.asParameter(1)` is the parameter `x` represents + * the value of `*x`. + * - The node `n` such that `n.asParameter(2)` is the parameter `x` represents + * the value of `**x`. + */ + Parameter asParameter(int index) { + index = 0 and + result = this.(ExplicitParameterNode).getParameter() + or + this.(IndirectParameterNode).getIndirectionIndex() = index and + result = this.(IndirectParameterNode).getParameter() + } + + /** + * Holds if this node represents the `indirectionIndex`'th indirection of + * the value of an output parameter `p` just before reaching the end of a function. + */ + predicate isFinalValueOfParameter(Parameter p, int indirectionIndex) { + exists(FinalParameterNode n | n = this | + p = n.getParameter() and + indirectionIndex = n.getIndirectionIndex() + ) + } + + /** + * Holds if this node represents the value of an output parameter `p` + * just before reaching the end of a function. + */ + predicate isFinalValueOfParameter(Parameter p) { this.isFinalValueOfParameter(p, _) } + + /** + * Gets the variable corresponding to this node, if any. This can be used for + * modeling flow in and out of global variables. + */ + Variable asVariable() { + this = TGlobalLikeVariableNode(result, getMinIndirectionsForType(result.getUnspecifiedType())) + } + + /** + * Gets the `indirectionIndex`'th indirection of this node's underlying variable, if any. + * + * This can be used for modeling flow in and out of global variables. + */ + Variable asIndirectVariable(int indirectionIndex) { + indirectionIndex > getMinIndirectionsForType(result.getUnspecifiedType()) and + this = TGlobalLikeVariableNode(result, indirectionIndex) + } + + /** Gets an indirection of this node's underlying variable, if any. */ + Variable asIndirectVariable() { result = this.asIndirectVariable(_) } + + /** + * Gets the expression that is partially defined by this node, if any. + * + * Partial definitions are created for field stores (`x.y = taint();` is a partial + * definition of `x`), and for calls that may change the value of an object (so + * `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is + * a partial definition of `&x`). + */ + Expr asPartialDefinition() { + exists(PartialDefinitionNode pdn | this = pdn | + pdn.getIndirectionIndex() > 0 and + result = pdn.getDefinedExpr() + ) + } + + /** + * Gets an upper bound on the type of this node. + */ + Type getTypeBound() { result = this.getType() } + + /** Gets the location of this element. */ + final Location getLocation() { result = getLocationCached(this) } + + /** INTERNAL: Do not use. */ + Location getLocationImpl() { + none() // overridden by subclasses + } + + /** Gets a textual representation of this element. */ + final string toString() { result = toStringCached(this) } + + /** INTERNAL: Do not use. */ + string toStringImpl() { + none() // overridden by subclasses + } + } + + /** + * An instruction, viewed as a node in a data flow graph. + */ + class InstructionNode extends Node0 { + override InstructionNode0 node; + Instruction instr; + + InstructionNode() { instr = node.getInstruction() } + + /** Gets the instruction corresponding to this node. */ + Instruction getInstruction() { result = instr } + } + + /** + * An operand, viewed as a node in a data flow graph. + */ + class OperandNode extends Node, Node0 { + override OperandNode0 node; + Operand op; + + OperandNode() { op = node.getOperand() } + + /** Gets the operand corresponding to this node. */ + Operand getOperand() { result = op } + } + + /** + * A node associated with an object after an operation that might have + * changed its state. + * + * This can be either the argument to a callable after the callable returns + * (which might have mutated the argument), or the qualifier of a field after + * an update to the field. + * + * Nodes corresponding to AST elements, for example `ExprNode`, usually refer + * to the value before the update with the exception of `ClassInstanceExpr`, + * which represents the value after the constructor has run. + */ + abstract class PostUpdateNode extends Node { + /** + * Gets the node before the state update. + */ + abstract Node getPreUpdateNode(); + + final override Type getType() { result = this.getPreUpdateNode().getType() } + } + + /** + * The value of an uninitialized local variable, viewed as a node in a data + * flow graph. + */ + class UninitializedNode extends Node { + LocalVariable v; + + UninitializedNode() { + exists(SsaImpl::Definition def, SsaImpl::SourceVariable sv | + def.getIndirectionIndex() = 0 and + def.getValue().asInstruction() instanceof UninitializedInstruction and + SsaImpl::defToNode(this, def, sv) and + v = sv.getBaseVariable().(SsaImpl::BaseIRVariable).getIRVariable().getAst() + ) + } + + /** Gets the uninitialized local variable corresponding to this node. */ + LocalVariable getLocalVariable() { result = v } + } + + /** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. This includes both explicit parameters such as `x` in `f(x)` + * and implicit parameters such as `this` in `x.f()`. + * + * To match a specific kind of parameter, consider using one of the subclasses + * `ExplicitParameterNode`, `ThisParameterNode`, or + * `ParameterIndirectionNode`. + */ + final class ParameterNode = AbstractParameterNode; + + /** An explicit positional parameter, including `this`, but not `...`. */ + final class DirectParameterNode = AbstractDirectParameterNode; + + final class ExplicitParameterNode = AbstractExplicitParameterNode; + + /** An implicit `this` parameter. */ + class ThisParameterInstructionNode extends AbstractExplicitParameterNode, + InstructionDirectParameterNode + { + ThisParameterInstructionNode() { instr.getIRVariable() instanceof IRThisVariable } + + override predicate isSourceParameterOf(Function f, ParameterPosition pos) { + pos.(DirectPosition).getArgumentIndex() = -1 and + instr.getEnclosingFunction() = f + } + + override string toStringImpl() { result = "this" } + } + + /** + * 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. + * + * A typical example would be a call `f(&x)`. Firstly, there will be flow into + * `x` from previous definitions of `x`. Secondly, there will be a + * `DefinitionByReferenceNode` to represent the value of `x` after the call has + * returned. This node will have its `getArgument()` equal to `&x` and its + * `getVariableAccess()` equal to `x`. + */ + class DefinitionByReferenceNode extends IndirectArgumentOutNode { + DefinitionByReferenceNode() { this.getIndirectionIndex() > 0 } + + /** Gets the unconverted argument corresponding to this node. */ + Expr getArgument() { + result = this.getAddressOperand().getDef().getUnconvertedResultExpression() + } + + /** Gets the parameter through which this value is assigned. */ + Parameter getParameter() { + result = this.getCallInstruction().getStaticCallTarget().getParameter(this.getArgumentIndex()) + } + } + + /** + * A `Node` corresponding to a global (or `static` local) variable in the + * program, as opposed to the value of that variable at some particular point. + * This is used to model flow through global variables (and `static` local + * variables). + * + * There is no `VariableNode` for non-`static` local variables. + */ + class VariableNode extends Node, TGlobalLikeVariableNode { + Variable v; + int indirectionIndex; + + VariableNode() { this = TGlobalLikeVariableNode(v, indirectionIndex) } + + /** Gets the variable corresponding to this node. */ + Variable getVariable() { result = v } + + /** Gets the indirection index of this node. */ + int getIndirectionIndex() { result = indirectionIndex } + + override Declaration getFunction() { none() } + + override DataFlowCallable getEnclosingCallable() { + // 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. + // Therefore we want a distinct enclosing callable for each `VariableNode`, + // and that can be the `Variable` itself. + result.asSourceCallable() = v + } + + override Type getType() { result = getTypeImpl(v.getUnderlyingType(), indirectionIndex - 1) } + + final override Location getLocationImpl() { + // Certain variables (such as parameters) can have multiple locations. + // When there's a unique location we use that one, but if multiple locations + // exist we default to an unknown location. + result = unique( | | v.getLocation()) + or + not exists(unique( | | v.getLocation())) and + result instanceof UnknownLocation + } + + override string toStringImpl() { result = stars(this) + v.toString() } + } + + /** + * Gets the node corresponding to `instr`. + */ + InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr } + + /** + * Gets the node corresponding to `operand`. + */ + OperandNode operandNode(Operand operand) { result.getOperand() = operand } + + /** + * Gets the `Node` corresponding to the value of evaluating `e` or any of its + * conversions. There is no result if `e` is a `Conversion`. For data flowing + * _out of_ an expression, like when an argument is passed by reference, use + * `definitionByReferenceNodeFromArgument` instead. + */ + ExprNode exprNode(Expr e) { result.getExpr(_) = e } + + /** + * Gets the `Node` corresponding to the value of evaluating `e`. Here, `e` may + * be a `Conversion`. For data flowing _out of_ an expression, like when an + * argument is passed by reference, use + * `definitionByReferenceNodeFromArgument` instead. + */ + ExprNode convertedExprNode(Expr e) { result.getConvertedExpr(_) = e } + + /** + * Gets the `Node` corresponding to the value of `p` at function entry. + */ + ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p } + + /** + * Gets the `Node` corresponding to a definition by reference of the variable + * that is passed as unconverted `argument` of a call. + */ + DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) { + result.getArgument() = argument + } + + /** Gets the `VariableNode` corresponding to the variable `v`. */ + VariableNode variableNode(Variable v) { + result.getVariable() = v and result.getIndirectionIndex() = 1 + } + + /** + * DEPRECATED: See UninitializedNode. + * + * Gets the `Node` corresponding to the value of an uninitialized local + * variable `v`. + */ + Node uninitializedNode(LocalVariable v) { none() } + + predicate hasOperandAndIndex( + IndirectOperand indirectOperand, Operand operand, int indirectionIndex + ) { + indirectOperand.hasOperandAndIndirectionIndex(operand, indirectionIndex) + } + + predicate hasInstructionAndIndex( + IndirectInstruction indirectInstr, Instruction instr, int indirectionIndex + ) { + indirectInstr.hasInstructionAndIndirectionIndex(instr, indirectionIndex) + } +} + +private import Public + +/** + * A node representing an indirection of a parameter. + */ +final class IndirectParameterNode = AbstractIndirectParameterNode; + +/** + * A class that lifts pre-SSA dataflow nodes to regular dataflow nodes. + */ +private class Node0 extends Node, TNode0 { + Node0Impl node; + + Node0() { this = TNode0(node) } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = node.getEnclosingCallable() + } + + override Declaration getFunction() { result = node.getFunction() } + + override Location getLocationImpl() { result = node.getLocation() } + + override string toStringImpl() { result = node.toString() } + + override Type getType() { result = node.getType() } + + override predicate isGLValue() { node.isGLValue() } +} + +class PostUpdateNodeImpl extends PartialDefinitionNode, TPostUpdateNodeImpl { + int indirectionIndex; + Operand operand; + + PostUpdateNodeImpl() { this = TPostUpdateNodeImpl(operand, indirectionIndex) } + + override Declaration getFunction() { result = operand.getUse().getEnclosingFunction() } + + override DataFlowCallable getEnclosingCallable() { + result = this.getPreUpdateNode().getEnclosingCallable() + } + + /** Gets the operand associated with this node. */ + Operand getOperand() { result = operand } + + /** Gets the indirection index associated with this node. */ + override int getIndirectionIndex() { result = indirectionIndex } + + override Location getLocationImpl() { result = operand.getLocation() } + + final override Node getPreUpdateNode() { + indirectionIndex > 0 and + hasOperandAndIndex(result, operand, indirectionIndex) + or + indirectionIndex = 0 and + result.asOperand() = operand + } + + final override Expr getDefinedExpr() { + result = operand.getDef().getUnconvertedResultExpression() + } +} + +/** + * The node representing the value of a field after it has been updated. + */ +class PostFieldUpdateNode extends PostUpdateNodeImpl { + FieldAddress fieldAddress; + + PostFieldUpdateNode() { operand = fieldAddress.getObjectAddressOperand() } + + FieldAddress getFieldAddress() { result = fieldAddress } + + Field getUpdatedField() { result = this.getFieldAddress().getField() } + + override string toStringImpl() { result = this.getPreUpdateNode() + " [post update]" } +} + +/** + * The base class for nodes that perform "partial definitions". + * + * In contrast to a normal "definition", which provides a new value for + * something, a partial definition is an expression that may affect a + * value, but does not necessarily replace it entirely. For example: + * ``` + * x.y = 1; // a partial definition of the object `x`. + * x.y.z = 1; // a partial definition of the object `x.y` and `x`. + * x.setY(1); // a partial definition of the object `x`. + * setY(&x); // a partial definition of the object `x`. + * ``` + */ +abstract private class PartialDefinitionNode extends PostUpdateNode { + /** Gets the indirection index of this node. */ + abstract int getIndirectionIndex(); + + /** Gets the expression that is partially defined by this node. */ + abstract Expr getDefinedExpr(); +} + +/** + * A node representing the indirection of a value after it + * has been returned from a function. + */ +class IndirectArgumentOutNode extends PostUpdateNodeImpl { + override ArgumentOperand operand; + + int getArgumentIndex() { + exists(CallInstruction call | call.getArgumentOperand(result) = operand) + } + + Operand getAddressOperand() { result = operand } + + 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() { + exists(string prefix | if indirectionIndex > 0 then prefix = "" else prefix = "pointer to " | + // This string should be unique enough to be helpful but common enough to + // avoid storing too many different strings. + result = prefix + this.getStaticCallTarget().getName() + " output argument" + or + not exists(this.getStaticCallTarget()) and + result = prefix + "output argument" + ) + } +} + +/** + * Holds if `node` is an indirect operand with columns `(operand, indirectionIndex)`, and + * `operand` represents a use of the fully converted value of `call`. + */ +private predicate hasOperand(Node node, CallInstruction call, int indirectionIndex, Operand operand) { + operandForFullyConvertedCall(operand, call) and + hasOperandAndIndex(node, operand, indirectionIndex) +} + +/** + * Holds if `node` is an indirect instruction with columns `(instr, indirectionIndex)`, and + * `instr` represents a use of the fully converted value of `call`. + * + * Note that `hasOperand(node, _, _, _)` implies `not hasInstruction(node, _, _, _)`. + */ +private predicate hasInstruction( + Node node, CallInstruction call, int indirectionIndex, Instruction instr +) { + instructionForFullyConvertedCall(instr, call) and + hasInstructionAndIndex(node, instr, indirectionIndex) +} + +/** + * A node representing the indirect value of a function call (i.e., a value hidden + * behind a number of indirections). + */ +class IndirectReturnOutNode extends Node { + CallInstruction call; + int indirectionIndex; + + IndirectReturnOutNode() { + // Annoyingly, we need to pick the fully converted value as the output of the function to + // make flow through in the shared dataflow library work correctly. + hasOperand(this, call, indirectionIndex, _) + or + hasInstruction(this, call, indirectionIndex, _) + } + + CallInstruction getCallInstruction() { result = call } + + int getIndirectionIndex() { result = indirectionIndex } + + /** Gets the operand associated with this node, if any. */ + Operand getOperand() { hasOperand(this, call, indirectionIndex, result) } + + /** Gets the instruction associated with this node, if any. */ + Instruction getInstruction() { hasInstruction(this, call, indirectionIndex, result) } +} + +/** + * An `IndirectReturnOutNode` which is used as a destination of a store operation. + * When it's used for a store operation it's useful to have this be a `PostUpdateNode` for + * the shared dataflow library's flow-through mechanism to detect flow in cases such as: + * ```cpp + * struct MyInt { + * int i; + * int& getRef() { return i; } + * }; + * ... + * MyInt mi; + * mi.getRef() = source(); // this is detected as a store to `i` via flow-through. + * sink(mi.i); + * ``` + */ +private class PostIndirectReturnOutNode extends IndirectReturnOutNode, PostUpdateNode { + PostIndirectReturnOutNode() { + any(StoreInstruction store).getDestinationAddressOperand() = this.getOperand() + } + + override Node getPreUpdateNode() { result = this } +} + +private module RawIndirectNodes { + /** + * A node that represents the indirect value of an operand in the IR + * after `index` number of loads. + */ + private class RawIndirectOperand0 extends Node, TRawIndirectOperand0 { + Node0Impl node; + int indirectionIndex; + + RawIndirectOperand0() { this = TRawIndirectOperand0(node, indirectionIndex) } + + /** Gets the underlying instruction. */ + Operand getOperand() { result = node.asOperand() } + + /** Gets the underlying indirection index. */ + int getIndirectionIndex() { result = indirectionIndex } + + override Declaration getFunction() { result = node.getFunction() } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = node.getEnclosingCallable() + } + + override predicate isGLValue() { this.getOperand().isGLValue() } + + override Type getType() { + exists(int sub, Type type, boolean isGLValue | + type = getOperandType(this.getOperand(), isGLValue) and + if isGLValue = true then sub = 1 else sub = 0 + | + result = getTypeImpl(type.getUnderlyingType(), indirectionIndex - sub) + ) + } + + final override Location getLocationImpl() { + if exists(this.getOperand().getLocation()) + then result = this.getOperand().getLocation() + else result instanceof UnknownLocation + } + + override string toStringImpl() { + result = stars(this) + operandNode(this.getOperand()).toStringImpl() + } + } + + /** + * A node that represents the indirect value of an instruction in the IR + * after `index` number of loads. + */ + private class RawIndirectInstruction0 extends Node, TRawIndirectInstruction0 { + Node0Impl node; + int indirectionIndex; + + RawIndirectInstruction0() { this = TRawIndirectInstruction0(node, indirectionIndex) } + + /** Gets the underlying instruction. */ + Instruction getInstruction() { result = node.asInstruction() } + + /** Gets the underlying indirection index. */ + int getIndirectionIndex() { result = indirectionIndex } + + override Declaration getFunction() { result = node.getFunction() } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = node.getEnclosingCallable() + } + + override predicate isGLValue() { this.getInstruction().isGLValue() } + + override Type getType() { + exists(int sub, Type type, boolean isGLValue | + type = getInstructionType(this.getInstruction(), isGLValue) and + if isGLValue = true then sub = 1 else sub = 0 + | + result = getTypeImpl(type.getUnderlyingType(), indirectionIndex - sub) + ) + } + + final override Location getLocationImpl() { + if exists(this.getInstruction().getLocation()) + then result = this.getInstruction().getLocation() + else result instanceof UnknownLocation + } + + override string toStringImpl() { + result = stars(this) + instructionNode(this.getInstruction()).toStringImpl() + } + } + + /** + * A node that represents the indirect value of an operand in the IR + * after a number of loads. + */ + class RawIndirectOperand extends Node { + int indirectionIndex; + Operand operand; + + RawIndirectOperand() { + exists(Node0Impl node | operand = node.asOperand() | + this = TRawIndirectOperand0(node, indirectionIndex) + or + this = TRawIndirectInstruction0(node, indirectionIndex) + ) + } + + /** Gets the operand associated with this node. */ + Operand getOperand() { result = operand } + + /** Gets the underlying indirection index. */ + int getIndirectionIndex() { result = indirectionIndex } + } + + /** + * A node that represents the indirect value of an instruction in the IR + * after a number of loads. + */ + class RawIndirectInstruction extends Node { + int indirectionIndex; + Instruction instr; + + RawIndirectInstruction() { + exists(Node0Impl node | instr = node.asInstruction() | + this = TRawIndirectOperand0(node, indirectionIndex) + or + this = TRawIndirectInstruction0(node, indirectionIndex) + ) + } + + /** Gets the instruction associated with this node. */ + Instruction getInstruction() { result = instr } + + /** Gets the underlying indirection index. */ + int getIndirectionIndex() { result = indirectionIndex } + } +} + +import RawIndirectNodes + +/** + * A synthesized SSA node produced by the shared SSA library, viewed as a node + * in a data flow graph. + */ +class SsaSynthNode extends Node, TSsaSynthNode { + SsaImpl::SynthNode node; + + SsaSynthNode() { this = TSsaSynthNode(node) } + + /** Gets the synthesized SSA node associated with this node. */ + SsaImpl::SynthNode getSynthNode() { result = node } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getFunction() + } + + override Declaration getFunction() { result = node.getBasicBlock().getEnclosingFunction() } + + override Type getType() { result = node.getSourceVariable().getType() } + + override predicate isGLValue() { node.getSourceVariable().isGLValue() } + + final override Location getLocationImpl() { result = node.getLocation() } + + override string toStringImpl() { result = node.toString() } +} + +/** + * Dataflow nodes necessary for iterator flow + */ +class SsaIteratorNode extends Node, TSsaIteratorNode { + IteratorFlow::IteratorFlowNode node; + + SsaIteratorNode() { this = TSsaIteratorNode(node) } + + /** Gets the phi node associated with this node. */ + IteratorFlow::IteratorFlowNode getIteratorFlowNode() { result = node } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getFunction() + } + + override Declaration getFunction() { result = node.getFunction() } + + override Type getType() { result = node.getType() } + + final override Location getLocationImpl() { result = node.getLocation() } + + override string toStringImpl() { result = node.toString() } +} + +/** + * A node representing a value after leaving a function. + */ +class SideEffectOperandNode extends Node instanceof IndirectOperand { + CallInstruction call; + int argumentIndex; + ArgumentOperand arg; + + SideEffectOperandNode() { + arg = call.getArgumentOperand(argumentIndex) and + IndirectOperand.super.hasOperandAndIndirectionIndex(arg, _) + } + + CallInstruction getCallInstruction() { result = call } + + /** Gets the underlying operand and the underlying indirection index. */ + predicate hasAddressOperandAndIndirectionIndex(Operand operand, int indirectionIndex) { + IndirectOperand.super.hasOperandAndIndirectionIndex(operand, indirectionIndex) + } + + int getArgumentIndex() { result = argumentIndex } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getFunction() + } + + override Declaration getFunction() { result = call.getEnclosingFunction() } + + Expr getArgument() { result = call.getArgument(argumentIndex).getUnconvertedResultExpression() } +} + +/** + * A node representing the value of a global variable just before returning + * from a function body. + */ +class FinalGlobalValue extends Node, TFinalGlobalValue { + SsaImpl::GlobalUse globalUse; + + FinalGlobalValue() { this = TFinalGlobalValue(globalUse) } + + /** Gets the underlying SSA use. */ + SsaImpl::GlobalUse getGlobalUse() { result = globalUse } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getFunction() + } + + override Declaration getFunction() { result = globalUse.getIRFunction().getFunction() } + + override Type getType() { + exists(int indirectionIndex | + indirectionIndex = globalUse.getIndirectionIndex() and + result = getTypeImpl(globalUse.getUnderlyingType(), indirectionIndex) + ) + } + + final override Location getLocationImpl() { result = globalUse.getLocation() } + + override string toStringImpl() { result = globalUse.toString() } +} + +/** + * A node representing the value of a global variable just after entering + * a function body. + */ +class InitialGlobalValue extends Node, TInitialGlobalValue { + SsaImpl::GlobalDef globalDef; + + InitialGlobalValue() { this = TInitialGlobalValue(globalDef) } + + /** Gets the underlying SSA definition. */ + SsaImpl::GlobalDef getGlobalDef() { result = globalDef } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getFunction() + } + + override Declaration getFunction() { result = globalDef.getFunction() } + + final override predicate isGLValue() { globalDef.getIndirectionIndex() = 0 } + + override Type getType() { result = globalDef.getUnderlyingType() } + + final override Location getLocationImpl() { result = globalDef.getLocation() } + + override string toStringImpl() { result = globalDef.toString() } +} + +/** + * A node representing a parameter for a function with no body. + */ +class BodyLessParameterNodeImpl extends Node, TBodyLessParameterNodeImpl { + Parameter p; + int indirectionIndex; + + BodyLessParameterNodeImpl() { this = TBodyLessParameterNodeImpl(p, indirectionIndex) } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getFunction() + } + + override Declaration getFunction() { result = p.getFunction() } + + /** Gets the indirection index of this node. */ + int getIndirectionIndex() { result = indirectionIndex } + + override Type getType() { + result = getTypeImpl(p.getUnderlyingType(), this.getIndirectionIndex()) + } + + final override Location getLocationImpl() { + result = unique( | | p.getLocation()) + or + count(p.getLocation()) != 1 and + result instanceof UnknownLocation + } + + final override string toStringImpl() { + exists(string prefix | prefix = stars(this) | result = prefix + p.toString()) + } +} + +/** + * A data-flow node used to model flow summaries. That is, a dataflow node + * that is synthesized to represent a parameter, return value, or other part + * of a models-as-data modeled function. + */ +class FlowSummaryNode extends Node, TFlowSummaryNode { + /** + * Gets the models-as-data `SummaryNode` associated with this dataflow + * `FlowSummaryNode`. + */ + FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) } + + /** + * Gets the summarized callable that this node belongs to. + */ + FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() { + result = this.getSummaryNode().getSummarizedCallable() + } + + /** + * Gets the enclosing callable. For a `FlowSummaryNode` this is always the + * summarized function this node is part of. + */ + override DataFlowCallable getEnclosingCallable() { + result.asSummarizedCallable() = this.getSummarizedCallable() + } + + override Location getLocationImpl() { result = this.getSummarizedCallable().getLocation() } + + override string toStringImpl() { result = this.getSummaryNode().toString() } +} + +/** + * A node representing the indirection of a value that is + * about to be returned from a function. + */ +class IndirectReturnNode extends Node { + IndirectReturnNode() { + this instanceof FinalParameterNode + or + this.(IndirectOperand) + .hasOperandAndIndirectionIndex(any(ReturnValueInstruction ret).getReturnAddressOperand(), _) + } + + override SourceCallable getEnclosingCallable() { result.asSourceCallable() = this.getFunction() } + + /** + * Holds if this node represents the value that is returned to the caller + * through a `return` statement. + */ + predicate isNormalReturn() { this instanceof IndirectOperand } + + /** + * Holds if this node represents the value that is returned to the caller + * by writing to the `argumentIndex`'th argument of the call. + */ + predicate isParameterReturn(int argumentIndex) { + this.(FinalParameterNode).getArgumentIndex() = argumentIndex + } + + /** Gets the indirection index of this indirect return node. */ + int getIndirectionIndex() { + result = this.(FinalParameterNode).getIndirectionIndex() + or + this.(IndirectOperand).hasOperandAndIndirectionIndex(_, result) + } +} + +/** + * A node representing the value of an output parameter + * just before reaching the end of a function. + */ +class FinalParameterNode extends Node, TFinalParameterNode { + Parameter p; + int indirectionIndex; + + FinalParameterNode() { this = TFinalParameterNode(p, indirectionIndex) } + + /** Gets the parameter associated with this final use. */ + Parameter getParameter() { result = p } + + /** Gets the underlying indirection index. */ + int getIndirectionIndex() { result = indirectionIndex } + + /** Gets the argument index associated with this final use. */ + final int getArgumentIndex() { result = p.getIndex() } + + override Declaration getFunction() { result = p.getFunction() } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getFunction() + } + + override Type getType() { result = getTypeImpl(p.getUnderlyingType(), indirectionIndex) } + + final override Location getLocationImpl() { + // Parameters can have multiple locations. When there's a unique location we use + // that one, but if multiple locations exist we default to an unknown location. + result = unique( | | p.getLocation()) + or + not exists(unique( | | p.getLocation())) and + result instanceof UnknownLocation + } + + override string toStringImpl() { result = stars(this) + p.toString() } +} + +abstract private class AbstractParameterNode extends Node { + /** + * Holds if this node is the parameter of `f` at the specified position. The + * implicit `this` parameter is considered to have position `-1`, and + * pointer-indirection parameters are at further negative positions. + */ + predicate isSourceParameterOf(Function f, ParameterPosition pos) { none() } + + /** + * Holds if this node is the parameter of `sc` at the specified position. The + * implicit `this` parameter is considered to have position `-1`, and + * pointer-indirection parameters are at further negative positions. + */ + predicate isSummaryParameterOf( + FlowSummaryImpl::Public::SummarizedCallable sc, ParameterPosition pos + ) { + none() + } + + /** + * Holds if this node is the parameter of `c` at the specified position. The + * implicit `this` parameter is considered to have position `-1`, and + * pointer-indirection parameters are at further negative positions. + */ + final predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + this.isSummaryParameterOf(c.asSummarizedCallable(), pos) + or + this.isSourceParameterOf(c.asSourceCallable(), pos) + } + + /** Gets the `Parameter` associated with this node, if it exists. */ + Parameter getParameter() { none() } // overridden by subclasses +} + +abstract private class AbstractIndirectParameterNode extends AbstractParameterNode { + /** Gets the indirection index of this parameter node. */ + abstract int getIndirectionIndex(); +} + +pragma[noinline] +private predicate indirectParameterNodeHasArgumentIndexAndIndex( + IndirectInstructionParameterNode node, int argumentIndex, int indirectionIndex +) { + node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and + node.getArgumentIndex() = argumentIndex +} + +pragma[noinline] +private predicate indirectPositionHasArgumentIndexAndIndex( + IndirectionPosition pos, int argumentIndex, int indirectionIndex +) { + pos.getArgumentIndex() = argumentIndex and + pos.getIndirectionIndex() = indirectionIndex +} + +private class IndirectInstructionParameterNode extends AbstractIndirectParameterNode instanceof IndirectInstruction +{ + InitializeParameterInstruction init; + + IndirectInstructionParameterNode() { + IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) + } + + int getArgumentIndex() { init.hasIndex(result) } + + override string toStringImpl() { + exists(string prefix | prefix = stars(this) | + result = prefix + this.getParameter().toString() + or + not exists(this.getParameter()) and + result = prefix + "this" + ) + } + + /** Gets the parameter whose indirection is initialized. */ + override Parameter getParameter() { result = init.getParameter() } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getFunction() + } + + override Declaration getFunction() { result = init.getEnclosingFunction() } + + override predicate isSourceParameterOf(Function f, ParameterPosition pos) { + this.getFunction() = f and + exists(int argumentIndex, int indirectionIndex | + indirectPositionHasArgumentIndexAndIndex(pos, argumentIndex, indirectionIndex) and + indirectParameterNodeHasArgumentIndexAndIndex(this, argumentIndex, indirectionIndex) + ) + } + + /** Gets the underlying operand and the underlying indirection index. */ + predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) { + IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index) + } + + final override int getIndirectionIndex() { this.hasInstructionAndIndirectionIndex(init, result) } +} + +abstract private class AbstractDirectParameterNode extends AbstractParameterNode { } + +/** + * A non-indirect parameter node that is represented as an `Instruction`. + */ +abstract class InstructionDirectParameterNode extends InstructionNode, AbstractDirectParameterNode { + final override InitializeParameterInstruction instr; + + /** + * Gets the `IRVariable` that this parameter references. + */ + final IRVariable getIRVariable() { result = instr.getIRVariable() } +} + +abstract private class AbstractExplicitParameterNode extends AbstractDirectParameterNode { } + +/** An explicit positional parameter, not including `this` or `...`. */ +private class ExplicitParameterInstructionNode extends AbstractExplicitParameterNode, + InstructionDirectParameterNode +{ + ExplicitParameterInstructionNode() { exists(instr.getParameter()) } + + override predicate isSourceParameterOf(Function f, ParameterPosition pos) { + f.getParameter(pos.(DirectPosition).getArgumentIndex()) = instr.getParameter() + } + + override string toStringImpl() { result = instr.getParameter().toString() } + + override Parameter getParameter() { result = instr.getParameter() } +} + +/** + * A parameter node that is part of a summary. + */ +class SummaryParameterNode extends AbstractParameterNode, FlowSummaryNode { + SummaryParameterNode() { + FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), _) + } + + private ParameterPosition getPosition() { + FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), result) + } + + override predicate isSummaryParameterOf( + FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition p + ) { + c = this.getSummarizedCallable() and + p = this.getPosition() + } +} + +private class DirectBodyLessParameterNode extends AbstractExplicitParameterNode, + BodyLessParameterNodeImpl +{ + DirectBodyLessParameterNode() { indirectionIndex = 0 } + + override predicate isSourceParameterOf(Function f, ParameterPosition pos) { + this.getFunction() = f and + f.getParameter(pos.(DirectPosition).getArgumentIndex()) = p + } + + override Parameter getParameter() { result = p } +} + +private class IndirectBodyLessParameterNode extends AbstractIndirectParameterNode, + BodyLessParameterNodeImpl +{ + IndirectBodyLessParameterNode() { not this instanceof DirectBodyLessParameterNode } + + override predicate isSourceParameterOf(Function f, ParameterPosition pos) { + exists(int argumentPosition | + this.getFunction() = f and + f.getParameter(argumentPosition) = p and + indirectPositionHasArgumentIndexAndIndex(pos, argumentPosition, indirectionIndex) + ) + } + + override int getIndirectionIndex() { + result = BodyLessParameterNodeImpl.super.getIndirectionIndex() + } + + override Parameter getParameter() { result = p } +} + +/** + * 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()) + } +} + +/** + * Returns `t`, but stripped of the outermost pointer, reference, etc. + * + * For example, `stripPointers(int*&)` is `int*` and `stripPointers(int*)` is `int`. + */ +private Type stripPointer(Type t) { + result = any(SsaImpl::Indirection ind | ind.getType() = t).getBaseType() + or + result = t.(PointerToMemberType).getBaseType() + or + result = t.(FunctionPointerIshType).getBaseType() +} + +/** + * Returns `t`, but stripped of the outer-most `indirectionIndex` number of indirections. + */ +private Type getTypeImpl0(Type t, int indirectionIndex) { + indirectionIndex = 0 and + result = t + or + indirectionIndex > 0 and + exists(Type stripped | + stripped = stripPointer(t.stripTopLevelSpecifiers()) and + stripped.getUnspecifiedType() != t.getUnspecifiedType() and + result = getTypeImpl0(stripped, indirectionIndex - 1) + ) +} + +/** + * Returns `t`, but stripped of the outer-most `indirectionIndex` number of indirections. + * + * If `indirectionIndex` cannot be stripped off `t`, an `UnknownType` is returned. + */ +bindingset[t, indirectionIndex] +pragma[inline_late] +Type getTypeImpl(Type t, int indirectionIndex) { + result = getTypeImpl0(t, indirectionIndex) + or + not exists(getTypeImpl0(t, indirectionIndex)) and + result instanceof UnknownType +} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 88d7dd9faf19..6dd953b16ab5 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -1,5 +1,6 @@ private import cpp as Cpp private import DataFlowUtil +private import DataFlowNodes private import semmle.code.cpp.ir.IR private import DataFlowDispatch private import semmle.code.cpp.ir.internal.IRCppLanguage @@ -16,28 +17,42 @@ private import semmle.code.cpp.dataflow.ExternalFlow as External cached private module Cached { cached - module Nodes0 { - cached - newtype TIRDataFlowNode0 = - TInstructionNode0(Instruction i) { - not Ssa::ignoreInstruction(i) and - not exists(Operand op | - not Ssa::ignoreOperand(op) and i = Ssa::getIRRepresentationOfOperand(op) - ) and - // We exclude `void`-typed instructions because they cannot contain data. - // However, if the instruction is a glvalue, and their type is `void`, then the result - // type of the instruction is really `void*`, and thus we still want to have a dataflow - // node for it. - (not i.getResultType() instanceof VoidType or i.isGLValue()) - } or - TMultipleUseOperandNode0(Operand op) { - not Ssa::ignoreOperand(op) and not exists(Ssa::getIRRepresentationOfOperand(op)) - } or - TSingleUseOperandNode0(Operand op) { - not Ssa::ignoreOperand(op) and exists(Ssa::getIRRepresentationOfOperand(op)) - } + newtype TIRDataFlowNode0 = + TInstructionNode0(Instruction i) { + not Ssa::ignoreInstruction(i) and + not exists(Operand op | + not Ssa::ignoreOperand(op) and i = Ssa::getIRRepresentationOfOperand(op) + ) and + // We exclude `void`-typed instructions because they cannot contain data. + // However, if the instruction is a glvalue, and their type is `void`, then the result + // type of the instruction is really `void*`, and thus we still want to have a dataflow + // node for it. + (not i.getResultType() instanceof VoidType or i.isGLValue()) + } or + TMultipleUseOperandNode0(Operand op) { + not Ssa::ignoreOperand(op) and not exists(Ssa::getIRRepresentationOfOperand(op)) + } or + TSingleUseOperandNode0(Operand op) { + not Ssa::ignoreOperand(op) and exists(Ssa::getIRRepresentationOfOperand(op)) + } + + cached + string toStringCached(Node n) { + result = toExprString(n) + or + not exists(toExprString(n)) and + result = n.toStringImpl() } + cached + Location getLocationCached(Node n) { result = n.getLocationImpl() } + + cached + newtype TContentApprox = + TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or + TUnionApproxContent(string s) { unionHasApproxName(_, s) } or + TElementApproxContent() + /** * Gets an additional term that is added to the `join` and `branch` computations to reflect * an additional forward or backwards branching factor that is not taken into account @@ -59,38 +74,174 @@ private module Cached { result = countNumberOfBranchesUsingParameter(switch, p) ) } -} -import Cached -private import Nodes0 + cached + newtype TDataFlowCallable = + TSourceCallable(Cpp::Declaration decl) or + TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c) -/** - * A module for calculating the number of stars (i.e., `*`s) needed for various - * dataflow node `toString` predicates. - */ -module NodeStars { - private int getNumberOfIndirections(Node n) { - result = n.(RawIndirectOperand).getIndirectionIndex() - or - result = n.(RawIndirectInstruction).getIndirectionIndex() - or - result = n.(VariableNode).getIndirectionIndex() + cached + newtype TDataFlowCall = + TNormalCall(CallInstruction call) or + TSummaryCall( + FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver + ) { + FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) + } + + /** + * Holds if data can flow from `node1` to `node2` in a way that loses the + * calling context. For example, this would happen with flow through a + * global or static variable. + */ + cached + predicate jumpStep(Node n1, Node n2) { + exists(GlobalLikeVariable v | + exists(Ssa::GlobalUse globalUse | + v = globalUse.getVariable() and + n1.(FinalGlobalValue).getGlobalUse() = globalUse + | + globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and + v = n2.asVariable() + or + v = n2.asIndirectVariable(globalUse.getIndirection()) + ) + or + exists(Ssa::GlobalDef globalDef | + v = globalDef.getVariable() and + n2.(InitialGlobalValue).getGlobalDef() = globalDef + | + globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and + v = n1.asVariable() + or + v = n1.asIndirectVariable(globalDef.getIndirection()) + ) + ) or - result = n.(PostUpdateNodeImpl).getIndirectionIndex() + // models-as-data summarized flow + FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(), + n2.(FlowSummaryNode).getSummaryNode()) + } + + /** + * Holds if data can flow from `node1` to `node2` via an assignment to `f`. + * Thus, `node2` references an object with a field `f` that contains the + * value of `node1`. + * + * The boolean `certain` is true if the destination address does not involve + * any pointer arithmetic, and false otherwise. + */ + cached + predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) { + exists( + PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads, + StoreInstruction store, FieldContent fc + | + postFieldUpdate = node2 and + fc = c and + nodeHasInstruction(node1, pragma[only_bind_into](store), + pragma[only_bind_into](indirectionIndex1)) and + postFieldUpdate.getIndirectionIndex() = 1 and + numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(), + store.getDestinationAddressOperand(), numberOfLoads, certain) and + fc.getAField() = postFieldUpdate.getUpdatedField() and + getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads + ) or - result = n.(FinalParameterNode).getIndirectionIndex() + // models-as-data summarized flow + FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c, + node2.(FlowSummaryNode).getSummaryNode()) and + certain = true + } + + /** + * Holds if data can flow from `node1` to `node2` via an assignment to `f`. + * Thus, `node2` references an object with a field `f` that contains the + * value of `node1`. + */ + cached + predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) } + + /** + * Holds if data can flow from `node1` to `node2` via a read of `f`. + * Thus, `node1` references an object with a field `f` whose value ends up in + * `node2`. + */ + cached + predicate readStep(Node node1, ContentSet c, Node node2) { + exists( + FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc + | + fc = c and + nodeHasOperand(node2, operand, indirectionIndex2) and + // The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct + // in `storeStep`. + nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and + numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and + fc.getAField() = fa1.getField() and + getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads + ) or - result = n.(BodyLessParameterNodeImpl).getIndirectionIndex() + // models-as-data summarized flow + FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c, + node2.(FlowSummaryNode).getSummaryNode()) } /** - * Gets the number of stars (i.e., `*`s) needed to produce the `toString` - * output for `n`. + * Holds if values stored inside content `c` are cleared at node `n`. */ - string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) } + cached + predicate clearsContent(Node n, ContentSet c) { + n = + any(PostUpdateNode pun, Content d | + d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) + | + pun + ).getPreUpdateNode() and + ( + not exists(Operand op, Cpp::Operation p | + n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and + ( + p instanceof Cpp::AssignPointerAddExpr or + p instanceof Cpp::AssignPointerSubExpr or + p instanceof Cpp::CrementOperation + ) + | + p.getAnOperand() = op.getUse().getAst() + ) + or + forex(PostUpdateNode pun, Content d | + pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and + storeStepImpl(_, d, pun, true) and + pun.getPreUpdateNode() = n + | + c.(Content).getIndirectionIndex() = d.getIndirectionIndex() + ) + ) + } +} + +import Cached + +private int getNumberOfIndirections(Node n) { + result = n.(RawIndirectOperand).getIndirectionIndex() + or + result = n.(RawIndirectInstruction).getIndirectionIndex() + or + result = n.(VariableNode).getIndirectionIndex() + or + result = n.(PostUpdateNodeImpl).getIndirectionIndex() + or + result = n.(FinalParameterNode).getIndirectionIndex() + or + result = n.(BodyLessParameterNodeImpl).getIndirectionIndex() } -import NodeStars +/** + * Gets the number of stars (i.e., `*`s) needed to produce the `toString` + * output for `n`. + */ +string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) } /** * A cut-down `DataFlow::Node` class that does not depend on the output of SSA. @@ -828,85 +979,10 @@ private int getMinIndirectionForGlobalDef(Ssa::GlobalDef def) { result = getMinIndirectionsForType(def.getUnspecifiedType()) } -/** - * Holds if data can flow from `node1` to `node2` in a way that loses the - * calling context. For example, this would happen with flow through a - * global or static variable. - */ -predicate jumpStep(Node n1, Node n2) { - exists(GlobalLikeVariable v | - exists(Ssa::GlobalUse globalUse | - v = globalUse.getVariable() and - n1.(FinalGlobalValue).getGlobalUse() = globalUse - | - globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and - v = n2.asVariable() - or - v = n2.asIndirectVariable(globalUse.getIndirection()) - ) - or - exists(Ssa::GlobalDef globalDef | - v = globalDef.getVariable() and - n2.(InitialGlobalValue).getGlobalDef() = globalDef - | - globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and - v = n1.asVariable() - or - v = n1.asIndirectVariable(globalDef.getIndirection()) - ) - ) - or - // models-as-data summarized flow - FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(), - n2.(FlowSummaryNode).getSummaryNode()) -} - bindingset[c] pragma[inline_late] private int getIndirectionIndexLate(Content c) { result = c.getIndirectionIndex() } -/** - * Holds if data can flow from `node1` to `node2` via an assignment to `f`. - * Thus, `node2` references an object with a field `f` that contains the - * value of `node1`. - * - * The boolean `certain` is true if the destination address does not involve - * any pointer arithmetic, and false otherwise. This has to do with whether a - * store step can be used to clear a field (see `clearsContent`). - */ -predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) { - exists( - PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads, - StoreInstruction store, FieldContent fc - | - postFieldUpdate = node2 and - fc = c and - nodeHasInstruction(node1, pragma[only_bind_into](store), - pragma[only_bind_into](indirectionIndex1)) and - postFieldUpdate.getIndirectionIndex() = 1 and - numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(), - store.getDestinationAddressOperand(), numberOfLoads, certain) and - fc.getAField() = postFieldUpdate.getUpdatedField() and - getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads - ) - or - // models-as-data summarized flow - FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c, - node2.(FlowSummaryNode).getSummaryNode()) and - certain = true -} - -/** - * Holds if data can flow from `node1` to `node2` via an assignment to `f`. - * Thus, `node2` references an object with a field `f` that contains the - * value of `node1`. - */ -predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) } - -/** - * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like - * operations and exactly `n` `LoadInstruction` operations. - */ private predicate numberOfLoadsFromOperandRec( Operand operandFrom, Operand operandTo, int ind, boolean certain ) { @@ -957,63 +1033,6 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex) hasInstructionAndIndex(node, instr, indirectionIndex) } -/** - * Holds if data can flow from `node1` to `node2` via a read of `f`. - * Thus, `node1` references an object with a field `f` whose value ends up in - * `node2`. - */ -predicate readStep(Node node1, ContentSet c, Node node2) { - exists( - FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc - | - fc = c and - nodeHasOperand(node2, operand, indirectionIndex2) and - // The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct - // in `storeStep`. - nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and - numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and - fc.getAField() = fa1.getField() and - getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads - ) - or - // models-as-data summarized flow - FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c, - node2.(FlowSummaryNode).getSummaryNode()) -} - -/** - * Holds if values stored inside content `c` are cleared at node `n`. - */ -predicate clearsContent(Node n, ContentSet c) { - n = - any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun) - .getPreUpdateNode() and - ( - // The crement operations and pointer addition and subtraction self-assign. We do not - // want to clear the contents if it is indirectly pointed at by any of these operations, - // as part of the contents might still be accessible afterwards. If there is no such - // indirection clearing the contents is safe. - not exists(Operand op, Cpp::Operation p | - n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and - ( - p instanceof Cpp::AssignPointerAddExpr or - p instanceof Cpp::AssignPointerSubExpr or - p instanceof Cpp::CrementOperation - ) - | - p.getAnOperand() = op.getUse().getAst() - ) - or - forex(PostUpdateNode pun, Content d | - pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and - storeStepImpl(_, d, pun, true) and - pun.getPreUpdateNode() = n - | - c.(Content).getIndirectionIndex() = d.getIndirectionIndex() - ) - ) -} - /** * Holds if the value that is being tracked is expected to be stored inside content `c` * at node `n`. @@ -1046,11 +1065,6 @@ class CastNode extends Node { CastNode() { none() } // stub implementation } -cached -private newtype TDataFlowCallable = - TSourceCallable(Cpp::Declaration decl) or - TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c) - /** * A callable, which may be: * - a function (that may contain code) @@ -1134,15 +1148,6 @@ class DataFlowType extends TypeFinal { string toString() { result = "" } } -cached -private newtype TDataFlowCall = - TNormalCall(CallInstruction call) or - TSummaryCall( - FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver - ) { - FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) - } - private predicate summarizedCallableIsManual(SummarizedCallable sc) { sc.asSummarizedCallable().hasManualModel() } @@ -1523,12 +1528,6 @@ private predicate fieldHasApproxName(Field f, string s) { private predicate unionHasApproxName(Cpp::Union u, string s) { s = u.getName().charAt(0) } -cached -private newtype TContentApprox = - TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or - TUnionApproxContent(string s) { unionHasApproxName(_, s) } or - TElementApproxContent() - /** An approximated `Content`. */ class ContentApprox extends TContentApprox { string toString() { none() } // overridden in subclasses diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index d704c7d56d65..514518660de0 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -11,6 +11,7 @@ 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 TaintTrackingUtil as TaintTrackingUtil private import DataFlowPrivate private import ModelUtil private import SsaImpl as SsaImpl @@ -18,1702 +19,8 @@ private import DataFlowImplCommon as DataFlowImplCommon private import codeql.util.Unit private import Node0ToString private import DataFlowDispatch as DataFlowDispatch -import ExprNodes - -/** - * The IR dataflow graph consists of the following nodes: - * - `Node0`, which injects most instructions and operands directly into the - * dataflow graph. - * - `VariableNode`, which is used to model flow through global variables. - * - `PostUpdateNodeImpl`, which is used to model the state of an object after - * an update after a number of loads. - * - `SsaSynthNode`, which represents synthesized nodes as computed by the shared SSA - * library. - * - `RawIndirectOperand`, which represents the value of `operand` after - * loading the address a number of times. - * - `RawIndirectInstruction`, which represents the value of `instr` after - * loading the address a number of times. - */ -cached -private newtype TIRDataFlowNode = - TNode0(Node0Impl node) { DataFlowImplCommon::forceCachingInSameStage() } or - TGlobalLikeVariableNode(GlobalLikeVariable var, int indirectionIndex) { - indirectionIndex = - [getMinIndirectionsForType(var.getUnspecifiedType()) .. SsaImpl::getMaxIndirectionsForType(var.getUnspecifiedType())] - } or - TPostUpdateNodeImpl(Operand operand, int indirectionIndex) { - isPostUpdateNodeImpl(operand, indirectionIndex) - } or - TSsaSynthNode(SsaImpl::SynthNode n) or - TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or - TRawIndirectOperand0(Node0Impl node, int indirectionIndex) { - SsaImpl::hasRawIndirectOperand(node.asOperand(), indirectionIndex) - } or - TRawIndirectInstruction0(Node0Impl node, int indirectionIndex) { - not exists(node.asOperand()) and - SsaImpl::hasRawIndirectInstruction(node.asInstruction(), indirectionIndex) - } or - TFinalParameterNode(Parameter p, int indirectionIndex) { - exists(SsaImpl::FinalParameterUse use | - use.getParameter() = p and - use.getIndirectionIndex() = indirectionIndex - ) - } or - TFinalGlobalValue(SsaImpl::GlobalUse globalUse) or - TInitialGlobalValue(SsaImpl::GlobalDef globalUse) or - TBodyLessParameterNodeImpl(Parameter p, int indirectionIndex) { - // Rule out parameters of catch blocks. - not exists(p.getCatchBlock()) and - // We subtract one because `getMaxIndirectionsForType` returns the maximum - // indirection for a glvalue of a given type, and this doesn't apply to - // parameters. - indirectionIndex = [0 .. SsaImpl::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1] and - not any(InitializeParameterInstruction init).getParameter() = p - } or - TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) - -/** - * An operand that is defined by a `FieldAddressInstruction`. - */ -class FieldAddress extends Operand { - FieldAddressInstruction fai; - - FieldAddress() { fai = this.getDef() and not SsaImpl::ignoreOperand(this) } - - /** Gets the field associated with this instruction. */ - Field getField() { result = fai.getField() } - - /** Gets the instruction whose result provides the address of the object containing the field. */ - Instruction getObjectAddress() { result = fai.getObjectAddress() } - - /** Gets the operand that provides the address of the object containing the field. */ - Operand getObjectAddressOperand() { result = fai.getObjectAddressOperand() } -} - -/** - * Holds if `opFrom` is an operand whose value flows to the result of `instrTo`. - * - * `isPointerArith` is `true` if `instrTo` is a `PointerArithmeticInstruction` and `opFrom` - * is the left operand. - * - * `additional` is `true` if the conversion is supplied by an implementation of the - * `Indirection` class. It is sometimes useful to exclude such conversions. - */ -predicate conversionFlow( - Operand opFrom, Instruction instrTo, boolean isPointerArith, boolean additional -) { - isPointerArith = false and - ( - additional = false and - ( - instrTo.(CopyValueInstruction).getSourceValueOperand() = opFrom - or - instrTo.(ConvertInstruction).getUnaryOperand() = opFrom - or - instrTo.(CheckedConvertOrNullInstruction).getUnaryOperand() = opFrom - or - instrTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom - or - exists(BuiltInInstruction builtIn | - builtIn = instrTo and - // __builtin_bit_cast - builtIn.getBuiltInOperation() instanceof BuiltInBitCast and - opFrom = builtIn.getAnOperand() - ) - ) - or - additional = true and - SsaImpl::isAdditionalConversionFlow(opFrom, instrTo) - ) - or - isPointerArith = true and - additional = false and - instrTo.(PointerArithmeticInstruction).getLeftOperand() = opFrom -} - -/** - * A node in a data flow graph. - * - * A node can be either an expression, a parameter, or an uninitialized local - * variable. Such nodes are created with `DataFlow::exprNode`, - * `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively. - */ -class Node extends TIRDataFlowNode { - /** - * INTERNAL: Do not use. - */ - DataFlowCallable getEnclosingCallable() { none() } // overridden in subclasses - - /** Gets the function to which this node belongs, if any. */ - Declaration getFunction() { none() } // overridden in subclasses - - /** Holds if this node represents a glvalue. */ - predicate isGLValue() { none() } - - /** - * Gets the type of this node. - * - * If `isGLValue()` holds, then the type of this node - * should be thought of as "pointer to `getType()`". - */ - Type getType() { none() } // overridden in subclasses - - /** Gets the instruction corresponding to this node, if any. */ - Instruction asInstruction() { result = this.(InstructionNode).getInstruction() } - - /** Gets the operands corresponding to this node, if any. */ - Operand asOperand() { result = this.(OperandNode).getOperand() } - - /** - * Gets the operand that is indirectly tracked by this node behind `index` - * number of indirections. - */ - Operand asIndirectOperand(int index) { hasOperandAndIndex(this, result, index) } - - /** - * Holds if this node is at index `i` in basic block `block`. - * - * Note: Phi nodes are considered to be at index `-1`. - */ - final predicate hasIndexInBlock(IRBlock block, int i) { - this.asInstruction() = block.getInstruction(i) - or - this.asOperand().getUse() = block.getInstruction(i) - or - exists(SsaImpl::SynthNode ssaNode | - this.(SsaSynthNode).getSynthNode() = ssaNode and - ssaNode.getBasicBlock() = block and - ssaNode.getIndex() = i - ) - or - this.(RawIndirectOperand).getOperand().getUse() = block.getInstruction(i) - or - this.(RawIndirectInstruction).getInstruction() = block.getInstruction(i) - or - this.(PostUpdateNode).getPreUpdateNode().hasIndexInBlock(block, i) - } - - /** Gets the basic block of this node, if any. */ - final IRBlock getBasicBlock() { this.hasIndexInBlock(result, _) } - - /** - * Gets the non-conversion expression corresponding to this node, if any. - * This predicate only has a result on nodes that represent the value of - * evaluating the expression. For data flowing _out of_ an expression, like - * when an argument is passed by reference, use `asDefiningArgument` instead - * of `asExpr`. - * - * If this node strictly (in the sense of `asConvertedExpr`) corresponds to - * a `Conversion`, then the result is the underlying non-`Conversion` base - * expression. - */ - Expr asExpr() { result = this.asExpr(_) } - - /** - * INTERNAL: Do not use. - */ - Expr asExpr(int n) { result = this.(ExprNode).getExpr(n) } - - /** - * INTERNAL: Do not use. - */ - Expr asIndirectExpr(int n, int index) { result = this.(IndirectExprNode).getExpr(n, index) } - - /** - * Gets the non-conversion expression that's indirectly tracked by this node - * under `index` number of indirections. - */ - Expr asIndirectExpr(int index) { result = this.asIndirectExpr(_, index) } - - /** - * Gets the non-conversion expression that's indirectly tracked by this node - * behind a number of indirections. - */ - Expr asIndirectExpr() { result = this.asIndirectExpr(_) } - - /** - * Gets the expression corresponding to this node, if any. The returned - * expression may be a `Conversion`. - */ - Expr asConvertedExpr() { result = this.asConvertedExpr(_) } - - /** - * Gets the expression corresponding to this node, if any. The returned - * expression may be a `Conversion`. - */ - Expr asConvertedExpr(int n) { result = this.(ExprNode).getConvertedExpr(n) } - - /** - * INTERNAL: Do not use. - */ - Expr asIndirectConvertedExpr(int n, int index) { - result = this.(IndirectExprNode).getConvertedExpr(n, index) - } - - /** - * Gets the expression that's indirectly tracked by this node - * behind `index` number of indirections. - */ - Expr asIndirectConvertedExpr(int index) { result = this.asIndirectConvertedExpr(_, index) } - - /** - * Gets the expression that's indirectly tracked by this node behind a - * number of indirections. - */ - Expr asIndirectConvertedExpr() { result = this.asIndirectConvertedExpr(_) } - - /** - * Gets the argument that defines this `DefinitionByReferenceNode`, if any. - * This predicate should be used instead of `asExpr` when referring to the - * value of a reference argument _after_ the call has returned. For example, - * in `f(&x)`, this predicate will have `&x` as its result for the `Node` - * that represents the new value of `x`. - */ - Expr asDefiningArgument() { result = this.asDefiningArgument(_) } - - /** - * Gets the definition associated with this node, if any. - * - * For example, consider the following example - * ```cpp - * int x = 42; // 1 - * x = 34; // 2 - * ++x; // 3 - * x++; // 4 - * x += 1; // 5 - * int y = x += 2; // 6 - * ``` - * - For (1) the result is `42`. - * - For (2) the result is `x = 34`. - * - For (3) the result is `++x`. - * - For (4) the result is `x++`. - * - For (5) the result is `x += 1`. - * - For (6) there are two results: - * - For the definition generated by `x += 2` the result is `x += 2` - * - For the definition generated by `int y = ...` the result is - * also `x += 2`. - * - * For assignments, `node.asDefinition()` and `node.asExpr()` will both exist - * for the same dataflow node. However, for expression such as `x++` that - * both write to `x` and read the current value of `x`, `node.asDefinition()` - * will give the node corresponding to the value after the increment, and - * `node.asExpr()` will give the node corresponding to the value before the - * increment. For an example of this, consider the following: - * - * ```cpp - * sink(x++); - * ``` - * in the above program, there will not be flow from a node `n` such that - * `n.asDefinition() instanceof IncrementOperation` to the argument of `sink` - * since the value passed to `sink` is the value before to the increment. - * However, there will be dataflow from a node `n` such that - * `n.asExpr() instanceof IncrementOperation` since the result of evaluating - * the expression `x++` is passed to `sink`. - */ - Expr asDefinition() { result = this.asDefinition(_) } - - private predicate isCertainStore() { - exists(SsaImpl::Definition def | - SsaImpl::defToNode(this, def, _) and - def.isCertain() - ) - } - - /** - * Gets the definition associated with this node, if any. - * - * For example, consider the following example - * ```cpp - * int x = 42; // 1 - * x = 34; // 2 - * ++x; // 3 - * x++; // 4 - * x += 1; // 5 - * int y = x += 2; // 6 - * ``` - * - For (1) the result is `42`. - * - For (2) the result is `x = 34`. - * - For (3) the result is `++x`. - * - For (4) the result is `x++`. - * - For (5) the result is `x += 1`. - * - For (6) there are two results: - * - For the definition generated by `x += 2` the result is `x += 2` - * - For the definition generated by `int y = ...` the result is - * also `x += 2`. - * - * For assignments, `node.asDefinition(_)` and `node.asExpr()` will both exist - * for the same dataflow node. However, for expression such as `x++` that - * both write to `x` and read the current value of `x`, `node.asDefinition(_)` - * will give the node corresponding to the value after the increment, and - * `node.asExpr()` will give the node corresponding to the value before the - * increment. For an example of this, consider the following: - * - * ```cpp - * sink(x++); - * ``` - * in the above program, there will not be flow from a node `n` such that - * `n.asDefinition(_) instanceof IncrementOperation` to the argument of `sink` - * since the value passed to `sink` is the value before to the increment. - * However, there will be dataflow from a node `n` such that - * `n.asExpr() instanceof IncrementOperation` since the result of evaluating - * the expression `x++` is passed to `sink`. - * - * If `uncertain = false` then the definition is guaranteed to overwrite - * the entire buffer pointed to by the destination address of the definition. - * Otherwise, `uncertain = true`. - * - * For example, the write `int x; x = 42;` is guaranteed to overwrite all the - * bytes allocated to `x`, while the assignment `int p[10]; p[3] = 42;` has - * `uncertain = true` since the write will not overwrite the entire buffer - * pointed to by `p`. - */ - Expr asDefinition(boolean uncertain) { - exists(StoreInstruction store | - store = this.asInstruction() and - result = asDefinitionImpl(store) and - if this.isCertainStore() then uncertain = false else uncertain = true - ) - } - - /** - * Gets the definition associated with this node, if this node is a certain definition. - * - * See `Node.asDefinition/1` for a description of certain and uncertain definitions. - */ - Expr asCertainDefinition() { result = this.asDefinition(false) } - - /** - * Gets the definition associated with this node, if this node is an uncertain definition. - * - * See `Node.asDefinition/1` for a description of certain and uncertain definitions. - */ - Expr asUncertainDefinition() { result = this.asDefinition(true) } - - /** - * Gets the indirect definition at a given indirection corresponding to this - * node, if any. - * - * See the comments on `Node.asDefinition` for examples. - */ - Expr asIndirectDefinition(int indirectionIndex) { - exists(StoreInstruction store | - this.(IndirectInstruction).hasInstructionAndIndirectionIndex(store, indirectionIndex) and - result = asDefinitionImpl(store) - ) - } - - /** - * Gets the indirect definition at some indirection corresponding to this - * node, if any. - */ - Expr asIndirectDefinition() { result = this.asIndirectDefinition(_) } - - /** - * Gets the argument that defines this `DefinitionByReferenceNode`, if any. - * - * Unlike `Node::asDefiningArgument/0`, this predicate gets the node representing - * the value of the `index`'th indirection after leaving a function. For example, - * in: - * ```cpp - * void f(int**); - * ... - * int** x = ...; - * f(x); - * ``` - * The node `n` such that `n.asDefiningArgument(1)` is the argument `x` will - * contain the value of `*x` after `f` has returned, and the node `n` such that - * `n.asDefiningArgument(2)` is the argument `x` will contain the value of `**x` - * after the `f` has returned. - */ - Expr asDefiningArgument(int index) { - this.(DefinitionByReferenceNode).getIndirectionIndex() = index and - result = this.(DefinitionByReferenceNode).getArgument() - } - - /** - * Gets the the argument going into a function for a node that represents - * the indirect value of the argument after `index` loads. For example, in: - * ```cpp - * void f(int**); - * ... - * int** x = ...; - * f(x); - * ``` - * The node `n` such that `n.asIndirectArgument(1)` represents the value of - * `*x` going into `f`, and the node `n` such that `n.asIndirectArgument(2)` - * represents the value of `**x` going into `f`. - */ - Expr asIndirectArgument(int index) { - this.(SideEffectOperandNode).hasAddressOperandAndIndirectionIndex(_, index) and - result = this.(SideEffectOperandNode).getArgument() - } - - /** - * Gets the the argument going into a function for a node that represents - * the indirect value of the argument after any non-zero number of loads. - */ - Expr asIndirectArgument() { result = this.asIndirectArgument(_) } - - /** Gets the positional parameter corresponding to this node, if any. */ - Parameter asParameter() { - exists(int indirectionIndex | result = this.asParameter(indirectionIndex) | - if result.getUnspecifiedType() instanceof ReferenceType - then indirectionIndex = 1 - else indirectionIndex = 0 - ) - } - - /** - * Gets the uninitialized local variable corresponding to this node, if - * any. - */ - LocalVariable asUninitialized() { result = this.(UninitializedNode).getLocalVariable() } - - /** - * Gets the positional parameter corresponding to the node that represents - * the value of the parameter after `index` number of loads, if any. For - * example, in: - * ```cpp - * void f(int** x) { ... } - * ``` - * - The node `n` such that `n.asParameter(0)` is the parameter `x` represents - * the value of `x`. - * - The node `n` such that `n.asParameter(1)` is the parameter `x` represents - * the value of `*x`. - * - The node `n` such that `n.asParameter(2)` is the parameter `x` represents - * the value of `**x`. - */ - Parameter asParameter(int index) { - index = 0 and - result = this.(ExplicitParameterNode).getParameter() - or - this.(IndirectParameterNode).getIndirectionIndex() = index and - result = this.(IndirectParameterNode).getParameter() - } - - /** - * Holds if this node represents the `indirectionIndex`'th indirection of - * the value of an output parameter `p` just before reaching the end of a function. - */ - predicate isFinalValueOfParameter(Parameter p, int indirectionIndex) { - exists(FinalParameterNode n | n = this | - p = n.getParameter() and - indirectionIndex = n.getIndirectionIndex() - ) - } - - /** - * Holds if this node represents the value of an output parameter `p` - * just before reaching the end of a function. - */ - predicate isFinalValueOfParameter(Parameter p) { this.isFinalValueOfParameter(p, _) } - - /** - * Gets the variable corresponding to this node, if any. This can be used for - * modeling flow in and out of global variables. - */ - Variable asVariable() { - this = TGlobalLikeVariableNode(result, getMinIndirectionsForType(result.getUnspecifiedType())) - } - - /** - * Gets the `indirectionIndex`'th indirection of this node's underlying variable, if any. - * - * This can be used for modeling flow in and out of global variables. - */ - Variable asIndirectVariable(int indirectionIndex) { - indirectionIndex > getMinIndirectionsForType(result.getUnspecifiedType()) and - this = TGlobalLikeVariableNode(result, indirectionIndex) - } - - /** Gets an indirection of this node's underlying variable, if any. */ - Variable asIndirectVariable() { result = this.asIndirectVariable(_) } - - /** - * Gets the expression that is partially defined by this node, if any. - * - * Partial definitions are created for field stores (`x.y = taint();` is a partial - * definition of `x`), and for calls that may change the value of an object (so - * `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is - * a partial definition of `&x`). - */ - Expr asPartialDefinition() { - exists(PartialDefinitionNode pdn | this = pdn | - pdn.getIndirectionIndex() > 0 and - result = pdn.getDefinedExpr() - ) - } - - /** - * Gets an upper bound on the type of this node. - */ - Type getTypeBound() { result = this.getType() } - - /** Gets the location of this element. */ - cached - final Location getLocation() { result = this.getLocationImpl() } - - /** INTERNAL: Do not use. */ - Location getLocationImpl() { - none() // overridden by subclasses - } - - /** Gets a textual representation of this element. */ - cached - final string toString() { - result = toExprString(this) - or - not exists(toExprString(this)) and - result = this.toStringImpl() - } - - /** INTERNAL: Do not use. */ - string toStringImpl() { - none() // overridden by subclasses - } -} - -/** - * A class that lifts pre-SSA dataflow nodes to regular dataflow nodes. - */ -private class Node0 extends Node, TNode0 { - Node0Impl node; - - Node0() { this = TNode0(node) } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = node.getEnclosingCallable() - } - - override Declaration getFunction() { result = node.getFunction() } - - override Location getLocationImpl() { result = node.getLocation() } - - override string toStringImpl() { result = node.toString() } - - override Type getType() { result = node.getType() } - - override predicate isGLValue() { node.isGLValue() } -} - -/** - * An instruction, viewed as a node in a data flow graph. - */ -class InstructionNode extends Node0 { - override InstructionNode0 node; - Instruction instr; - - InstructionNode() { instr = node.getInstruction() } - - /** Gets the instruction corresponding to this node. */ - Instruction getInstruction() { result = instr } -} - -/** - * An operand, viewed as a node in a data flow graph. - */ -class OperandNode extends Node, Node0 { - override OperandNode0 node; - Operand op; - - OperandNode() { op = node.getOperand() } - - /** Gets the operand corresponding to this node. */ - Operand getOperand() { result = op } -} - -/** - * INTERNAL: Do not use. - * - * Returns `t`, but stripped of the outermost pointer, reference, etc. - * - * For example, `stripPointers(int*&)` is `int*` and `stripPointers(int*)` is `int`. - */ -Type stripPointer(Type t) { - result = any(SsaImpl::Indirection ind | ind.getType() = t).getBaseType() - or - result = t.(PointerToMemberType).getBaseType() - or - result = t.(FunctionPointerIshType).getBaseType() -} - -/** - * INTERNAL: Do not use. - */ -class PostUpdateNodeImpl extends PartialDefinitionNode, TPostUpdateNodeImpl { - int indirectionIndex; - Operand operand; - - PostUpdateNodeImpl() { this = TPostUpdateNodeImpl(operand, indirectionIndex) } - - override Declaration getFunction() { result = operand.getUse().getEnclosingFunction() } - - override DataFlowCallable getEnclosingCallable() { - result = this.getPreUpdateNode().getEnclosingCallable() - } - - /** Gets the operand associated with this node. */ - Operand getOperand() { result = operand } - - /** Gets the indirection index associated with this node. */ - override int getIndirectionIndex() { result = indirectionIndex } - - override Location getLocationImpl() { result = operand.getLocation() } - - final override Node getPreUpdateNode() { - indirectionIndex > 0 and - hasOperandAndIndex(result, operand, indirectionIndex) - or - indirectionIndex = 0 and - result.asOperand() = operand - } - - final override Expr getDefinedExpr() { - result = operand.getDef().getUnconvertedResultExpression() - } -} - -/** - * INTERNAL: do not use. - * - * The node representing the value of a field after it has been updated. - */ -class PostFieldUpdateNode extends PostUpdateNodeImpl { - FieldAddress fieldAddress; - - PostFieldUpdateNode() { operand = fieldAddress.getObjectAddressOperand() } - - FieldAddress getFieldAddress() { result = fieldAddress } - - Field getUpdatedField() { result = this.getFieldAddress().getField() } - - override string toStringImpl() { result = this.getPreUpdateNode() + " [post update]" } -} - -/** - * INTERNAL: do not use. - * - * A synthesized SSA node produced by the shared SSA library, viewed as a node - * in a data flow graph. - */ -class SsaSynthNode extends Node, TSsaSynthNode { - SsaImpl::SynthNode node; - - SsaSynthNode() { this = TSsaSynthNode(node) } - - /** Gets the synthesized SSA node associated with this node. */ - SsaImpl::SynthNode getSynthNode() { result = node } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = this.getFunction() - } - - override Declaration getFunction() { result = node.getBasicBlock().getEnclosingFunction() } - - override Type getType() { result = node.getSourceVariable().getType() } - - override predicate isGLValue() { node.getSourceVariable().isGLValue() } - - final override Location getLocationImpl() { result = node.getLocation() } - - override string toStringImpl() { result = node.toString() } -} - -/** - * INTERNAL: do not use. - * - * Dataflow nodes necessary for iterator flow - */ -class SsaIteratorNode extends Node, TSsaIteratorNode { - IteratorFlow::IteratorFlowNode node; - - SsaIteratorNode() { this = TSsaIteratorNode(node) } - - /** Gets the phi node associated with this node. */ - IteratorFlow::IteratorFlowNode getIteratorFlowNode() { result = node } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = this.getFunction() - } - - override Declaration getFunction() { result = node.getFunction() } - - override Type getType() { result = node.getType() } - - final override Location getLocationImpl() { result = node.getLocation() } - - override string toStringImpl() { result = node.toString() } -} - -/** - * INTERNAL: do not use. - * - * A node representing a value after leaving a function. - */ -class SideEffectOperandNode extends Node instanceof IndirectOperand { - CallInstruction call; - int argumentIndex; - ArgumentOperand arg; - - SideEffectOperandNode() { - arg = call.getArgumentOperand(argumentIndex) and - IndirectOperand.super.hasOperandAndIndirectionIndex(arg, _) - } - - CallInstruction getCallInstruction() { result = call } - - /** Gets the underlying operand and the underlying indirection index. */ - predicate hasAddressOperandAndIndirectionIndex(Operand operand, int indirectionIndex) { - IndirectOperand.super.hasOperandAndIndirectionIndex(operand, indirectionIndex) - } - - int getArgumentIndex() { result = argumentIndex } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = this.getFunction() - } - - override Declaration getFunction() { result = call.getEnclosingFunction() } - - Expr getArgument() { result = call.getArgument(argumentIndex).getUnconvertedResultExpression() } -} - -/** - * INTERNAL: do not use. - * - * A node representing the value of a global variable just before returning - * from a function body. - */ -class FinalGlobalValue extends Node, TFinalGlobalValue { - SsaImpl::GlobalUse globalUse; - - FinalGlobalValue() { this = TFinalGlobalValue(globalUse) } - - /** Gets the underlying SSA use. */ - SsaImpl::GlobalUse getGlobalUse() { result = globalUse } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = this.getFunction() - } - - override Declaration getFunction() { result = globalUse.getIRFunction().getFunction() } - - override Type getType() { - exists(int indirectionIndex | - indirectionIndex = globalUse.getIndirectionIndex() and - result = getTypeImpl(globalUse.getUnderlyingType(), indirectionIndex) - ) - } - - final override Location getLocationImpl() { result = globalUse.getLocation() } - - override string toStringImpl() { result = globalUse.toString() } -} - -/** - * INTERNAL: do not use. - * - * A node representing the value of a global variable just after entering - * a function body. - */ -class InitialGlobalValue extends Node, TInitialGlobalValue { - SsaImpl::GlobalDef globalDef; - - InitialGlobalValue() { this = TInitialGlobalValue(globalDef) } - - /** Gets the underlying SSA definition. */ - SsaImpl::GlobalDef getGlobalDef() { result = globalDef } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = this.getFunction() - } - - override Declaration getFunction() { result = globalDef.getFunction() } - - final override predicate isGLValue() { globalDef.getIndirectionIndex() = 0 } - - override Type getType() { result = globalDef.getUnderlyingType() } - - final override Location getLocationImpl() { result = globalDef.getLocation() } - - override string toStringImpl() { result = globalDef.toString() } -} - -/** - * INTERNAL: do not use. - * - * A node representing a parameter for a function with no body. - */ -class BodyLessParameterNodeImpl extends Node, TBodyLessParameterNodeImpl { - Parameter p; - int indirectionIndex; - - BodyLessParameterNodeImpl() { this = TBodyLessParameterNodeImpl(p, indirectionIndex) } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = this.getFunction() - } - - override Declaration getFunction() { result = p.getFunction() } - - /** Gets the indirection index of this node. */ - int getIndirectionIndex() { result = indirectionIndex } - - override Type getType() { - result = getTypeImpl(p.getUnderlyingType(), this.getIndirectionIndex()) - } - - final override Location getLocationImpl() { - result = unique( | | p.getLocation()) - or - count(p.getLocation()) != 1 and - result instanceof UnknownLocation - } - - final override string toStringImpl() { - exists(string prefix | prefix = stars(this) | result = prefix + p.toString()) - } -} - -/** - * A data-flow node used to model flow summaries. That is, a dataflow node - * that is synthesized to represent a parameter, return value, or other part - * of a models-as-data modeled function. - */ -class FlowSummaryNode extends Node, TFlowSummaryNode { - /** - * Gets the models-as-data `SummaryNode` associated with this dataflow - * `FlowSummaryNode`. - */ - FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) } - - /** - * Gets the summarized callable that this node belongs to. - */ - FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() { - result = this.getSummaryNode().getSummarizedCallable() - } - - /** - * Gets the enclosing callable. For a `FlowSummaryNode` this is always the - * summarized function this node is part of. - */ - override DataFlowCallable getEnclosingCallable() { - result.asSummarizedCallable() = this.getSummarizedCallable() - } - - override Location getLocationImpl() { result = this.getSummarizedCallable().getLocation() } - - override string toStringImpl() { result = this.getSummaryNode().toString() } -} - -/** - * INTERNAL: do not use. - * - * A node representing the indirection of a value that is - * about to be returned from a function. - */ -class IndirectReturnNode extends Node { - IndirectReturnNode() { - this instanceof FinalParameterNode - or - this.(IndirectOperand) - .hasOperandAndIndirectionIndex(any(ReturnValueInstruction ret).getReturnAddressOperand(), _) - } - - override SourceCallable getEnclosingCallable() { result.asSourceCallable() = this.getFunction() } - - /** - * Holds if this node represents the value that is returned to the caller - * through a `return` statement. - */ - predicate isNormalReturn() { this instanceof IndirectOperand } - - /** - * Holds if this node represents the value that is returned to the caller - * by writing to the `argumentIndex`'th argument of the call. - */ - predicate isParameterReturn(int argumentIndex) { - this.(FinalParameterNode).getArgumentIndex() = argumentIndex - } - - /** Gets the indirection index of this indirect return node. */ - int getIndirectionIndex() { - result = this.(FinalParameterNode).getIndirectionIndex() - or - this.(IndirectOperand).hasOperandAndIndirectionIndex(_, result) - } -} - -/** - * INTERNAL: do not use. - * - * A node representing the indirection of a value after it - * has been returned from a function. - */ -class IndirectArgumentOutNode extends PostUpdateNodeImpl { - override ArgumentOperand operand; - - int getArgumentIndex() { - exists(CallInstruction call | call.getArgumentOperand(result) = operand) - } - - Operand getAddressOperand() { result = operand } - - 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() { - exists(string prefix | if indirectionIndex > 0 then prefix = "" else prefix = "pointer to " | - // This string should be unique enough to be helpful but common enough to - // avoid storing too many different strings. - result = prefix + this.getStaticCallTarget().getName() + " output argument" - or - not exists(this.getStaticCallTarget()) and - result = prefix + "output argument" - ) - } -} - -/** - * Holds if `node` is an indirect operand with columns `(operand, indirectionIndex)`, and - * `operand` represents a use of the fully converted value of `call`. - */ -private predicate hasOperand(Node node, CallInstruction call, int indirectionIndex, Operand operand) { - operandForFullyConvertedCall(operand, call) and - hasOperandAndIndex(node, operand, indirectionIndex) -} - -/** - * Holds if `node` is an indirect instruction with columns `(instr, indirectionIndex)`, and - * `instr` represents a use of the fully converted value of `call`. - * - * Note that `hasOperand(node, _, _, _)` implies `not hasInstruction(node, _, _, _)`. - */ -private predicate hasInstruction( - Node node, CallInstruction call, int indirectionIndex, Instruction instr -) { - instructionForFullyConvertedCall(instr, call) and - hasInstructionAndIndex(node, instr, indirectionIndex) -} - -/** - * INTERNAL: do not use. - * - * A node representing the indirect value of a function call (i.e., a value hidden - * behind a number of indirections). - */ -class IndirectReturnOutNode extends Node { - CallInstruction call; - int indirectionIndex; - - IndirectReturnOutNode() { - // Annoyingly, we need to pick the fully converted value as the output of the function to - // make flow through in the shared dataflow library work correctly. - hasOperand(this, call, indirectionIndex, _) - or - hasInstruction(this, call, indirectionIndex, _) - } - - CallInstruction getCallInstruction() { result = call } - - int getIndirectionIndex() { result = indirectionIndex } - - /** Gets the operand associated with this node, if any. */ - Operand getOperand() { hasOperand(this, call, indirectionIndex, result) } - - /** Gets the instruction associated with this node, if any. */ - Instruction getInstruction() { hasInstruction(this, call, indirectionIndex, result) } -} - -/** - * An `IndirectReturnOutNode` which is used as a destination of a store operation. - * When it's used for a store operation it's useful to have this be a `PostUpdateNode` for - * the shared dataflow library's flow-through mechanism to detect flow in cases such as: - * ```cpp - * struct MyInt { - * int i; - * int& getRef() { return i; } - * }; - * ... - * MyInt mi; - * mi.getRef() = source(); // this is detected as a store to `i` via flow-through. - * sink(mi.i); - * ``` - */ -private class PostIndirectReturnOutNode extends IndirectReturnOutNode, PostUpdateNode { - PostIndirectReturnOutNode() { - any(StoreInstruction store).getDestinationAddressOperand() = this.getOperand() - } - - override Node getPreUpdateNode() { result = this } -} - -/** - * INTERNAL: Do not use. - * - * Returns `t`, but stripped of the outer-most `indirectionIndex` number of indirections. - */ -private Type getTypeImpl0(Type t, int indirectionIndex) { - indirectionIndex = 0 and - result = t - or - indirectionIndex > 0 and - exists(Type stripped | - stripped = stripPointer(t.stripTopLevelSpecifiers()) and - // We need to avoid the case where `stripPointer(t) = t` (which can happen - // on iterators that specify a `value_type` that is the iterator itself). - // Such a type would create an infinite loop otherwise. For these cases we - // simply don't produce a result for `getTypeImpl`. - // To be on the safe side, we check whether the _unspecified_ type has - // changed since this also prevents an infinite loop when `stripped` and - // `t` only differ by const'ness or volatile'ness. - stripped.getUnspecifiedType() != t.getUnspecifiedType() and - result = getTypeImpl0(stripped, indirectionIndex - 1) - ) -} - -/** - * INTERNAL: Do not use. - * - * Returns `t`, but stripped of the outer-most `indirectionIndex` number of indirections. - * - * If `indirectionIndex` cannot be stripped off `t`, an `UnknownType` is returned. - */ -bindingset[t, indirectionIndex] -pragma[inline_late] -Type getTypeImpl(Type t, int indirectionIndex) { - result = getTypeImpl0(t, indirectionIndex) - or - // If we cannot produce the right type we return an error type. - // This can sometimes happen when we don't know the real - // type of a void pointer. - not exists(getTypeImpl0(t, indirectionIndex)) and - result instanceof UnknownType -} - -private module RawIndirectNodes { - /** - * INTERNAL: Do not use. - * - * A node that represents the indirect value of an operand in the IR - * after `index` number of loads. - */ - private class RawIndirectOperand0 extends Node, TRawIndirectOperand0 { - Node0Impl node; - int indirectionIndex; - - RawIndirectOperand0() { this = TRawIndirectOperand0(node, indirectionIndex) } - - /** Gets the underlying instruction. */ - Operand getOperand() { result = node.asOperand() } - - /** Gets the underlying indirection index. */ - int getIndirectionIndex() { result = indirectionIndex } - - override Declaration getFunction() { result = node.getFunction() } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = node.getEnclosingCallable() - } - - override predicate isGLValue() { this.getOperand().isGLValue() } - - override Type getType() { - exists(int sub, Type type, boolean isGLValue | - type = getOperandType(this.getOperand(), isGLValue) and - if isGLValue = true then sub = 1 else sub = 0 - | - result = getTypeImpl(type.getUnderlyingType(), indirectionIndex - sub) - ) - } - - final override Location getLocationImpl() { - if exists(this.getOperand().getLocation()) - then result = this.getOperand().getLocation() - else result instanceof UnknownLocation - } - - override string toStringImpl() { - result = stars(this) + operandNode(this.getOperand()).toStringImpl() - } - } - - /** - * INTERNAL: Do not use. - * - * A node that represents the indirect value of an instruction in the IR - * after `index` number of loads. - */ - private class RawIndirectInstruction0 extends Node, TRawIndirectInstruction0 { - Node0Impl node; - int indirectionIndex; - - RawIndirectInstruction0() { this = TRawIndirectInstruction0(node, indirectionIndex) } - - /** Gets the underlying instruction. */ - Instruction getInstruction() { result = node.asInstruction() } - - /** Gets the underlying indirection index. */ - int getIndirectionIndex() { result = indirectionIndex } - - override Declaration getFunction() { result = node.getFunction() } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = node.getEnclosingCallable() - } - - override predicate isGLValue() { this.getInstruction().isGLValue() } - - override Type getType() { - exists(int sub, Type type, boolean isGLValue | - type = getInstructionType(this.getInstruction(), isGLValue) and - if isGLValue = true then sub = 1 else sub = 0 - | - result = getTypeImpl(type.getUnderlyingType(), indirectionIndex - sub) - ) - } - - final override Location getLocationImpl() { - if exists(this.getInstruction().getLocation()) - then result = this.getInstruction().getLocation() - else result instanceof UnknownLocation - } - - override string toStringImpl() { - result = stars(this) + instructionNode(this.getInstruction()).toStringImpl() - } - } - - /** - * INTERNAL: Do not use. - * - * A node that represents the indirect value of an operand in the IR - * after a number of loads. - */ - class RawIndirectOperand extends Node { - int indirectionIndex; - Operand operand; - - RawIndirectOperand() { - exists(Node0Impl node | operand = node.asOperand() | - this = TRawIndirectOperand0(node, indirectionIndex) - or - this = TRawIndirectInstruction0(node, indirectionIndex) - ) - } - - /** Gets the operand associated with this node. */ - Operand getOperand() { result = operand } - - /** Gets the underlying indirection index. */ - int getIndirectionIndex() { result = indirectionIndex } - } - - /** - * INTERNAL: Do not use. - * - * A node that represents the indirect value of an instruction in the IR - * after a number of loads. - */ - class RawIndirectInstruction extends Node { - int indirectionIndex; - Instruction instr; - - RawIndirectInstruction() { - exists(Node0Impl node | instr = node.asInstruction() | - this = TRawIndirectOperand0(node, indirectionIndex) - or - this = TRawIndirectInstruction0(node, indirectionIndex) - ) - } - - /** Gets the instruction associated with this node. */ - Instruction getInstruction() { result = instr } - - /** Gets the underlying indirection index. */ - int getIndirectionIndex() { result = indirectionIndex } - } -} - -import RawIndirectNodes - -/** - * INTERNAL: do not use. - * - * A node representing the value of an output parameter - * just before reaching the end of a function. - */ -class FinalParameterNode extends Node, TFinalParameterNode { - Parameter p; - int indirectionIndex; - - FinalParameterNode() { this = TFinalParameterNode(p, indirectionIndex) } - - /** Gets the parameter associated with this final use. */ - Parameter getParameter() { result = p } - - /** Gets the underlying indirection index. */ - int getIndirectionIndex() { result = indirectionIndex } - - /** Gets the argument index associated with this final use. */ - final int getArgumentIndex() { result = p.getIndex() } - - override Declaration getFunction() { result = p.getFunction() } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = this.getFunction() - } - - override Type getType() { result = getTypeImpl(p.getUnderlyingType(), indirectionIndex) } - - final override Location getLocationImpl() { - // Parameters can have multiple locations. When there's a unique location we use - // that one, but if multiple locations exist we default to an unknown location. - result = unique( | | p.getLocation()) - or - not exists(unique( | | p.getLocation())) and - result instanceof UnknownLocation - } - - override string toStringImpl() { result = stars(this) + p.toString() } -} - -/** - * The value of an uninitialized local variable, viewed as a node in a data - * flow graph. - */ -class UninitializedNode extends Node { - LocalVariable v; - - UninitializedNode() { - exists(SsaImpl::Definition def, SsaImpl::SourceVariable sv | - def.getIndirectionIndex() = 0 and - def.getValue().asInstruction() instanceof UninitializedInstruction and - SsaImpl::defToNode(this, def, sv) and - v = sv.getBaseVariable().(SsaImpl::BaseIRVariable).getIRVariable().getAst() - ) - } - - /** Gets the uninitialized local variable corresponding to this node. */ - LocalVariable getLocalVariable() { result = v } -} - -abstract private class AbstractParameterNode extends Node { - /** - * Holds if this node is the parameter of `f` at the specified position. The - * implicit `this` parameter is considered to have position `-1`, and - * pointer-indirection parameters are at further negative positions. - */ - predicate isSourceParameterOf(Function f, ParameterPosition pos) { none() } - - /** - * Holds if this node is the parameter of `sc` at the specified position. The - * implicit `this` parameter is considered to have position `-1`, and - * pointer-indirection parameters are at further negative positions. - */ - predicate isSummaryParameterOf( - FlowSummaryImpl::Public::SummarizedCallable sc, ParameterPosition pos - ) { - none() - } - - /** - * Holds if this node is the parameter of `c` at the specified position. The - * implicit `this` parameter is considered to have position `-1`, and - * pointer-indirection parameters are at further negative positions. - */ - final predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.isSummaryParameterOf(c.asSummarizedCallable(), pos) - or - this.isSourceParameterOf(c.asSourceCallable(), pos) - } - - /** Gets the `Parameter` associated with this node, if it exists. */ - Parameter getParameter() { none() } // overridden by subclasses -} - -abstract private class AbstractIndirectParameterNode extends AbstractParameterNode { - /** Gets the indirection index of this parameter node. */ - abstract int getIndirectionIndex(); -} - -/** - * INTERNAL: do not use. - * - * A node representing an indirection of a parameter. - */ -final class IndirectParameterNode = AbstractIndirectParameterNode; - -pragma[noinline] -private predicate indirectParameterNodeHasArgumentIndexAndIndex( - IndirectInstructionParameterNode node, int argumentIndex, int indirectionIndex -) { - node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and - node.getArgumentIndex() = argumentIndex -} - -pragma[noinline] -private predicate indirectPositionHasArgumentIndexAndIndex( - IndirectionPosition pos, int argumentIndex, int indirectionIndex -) { - pos.getArgumentIndex() = argumentIndex and - pos.getIndirectionIndex() = indirectionIndex -} - -private class IndirectInstructionParameterNode extends AbstractIndirectParameterNode instanceof IndirectInstruction -{ - InitializeParameterInstruction init; - - IndirectInstructionParameterNode() { - IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) - } - - int getArgumentIndex() { init.hasIndex(result) } - - override string toStringImpl() { - exists(string prefix | prefix = stars(this) | - result = prefix + this.getParameter().toString() - or - not exists(this.getParameter()) and - result = prefix + "this" - ) - } - - /** Gets the parameter whose indirection is initialized. */ - override Parameter getParameter() { result = init.getParameter() } - - override DataFlowCallable getEnclosingCallable() { - result.asSourceCallable() = this.getFunction() - } - - override Declaration getFunction() { result = init.getEnclosingFunction() } - - override predicate isSourceParameterOf(Function f, ParameterPosition pos) { - this.getFunction() = f and - exists(int argumentIndex, int indirectionIndex | - indirectPositionHasArgumentIndexAndIndex(pos, argumentIndex, indirectionIndex) and - indirectParameterNodeHasArgumentIndexAndIndex(this, argumentIndex, indirectionIndex) - ) - } - - /** Gets the underlying operand and the underlying indirection index. */ - predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) { - IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index) - } - - final override int getIndirectionIndex() { this.hasInstructionAndIndirectionIndex(init, result) } -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. This includes both explicit parameters such as `x` in `f(x)` - * and implicit parameters such as `this` in `x.f()`. - * - * To match a specific kind of parameter, consider using one of the subclasses - * `ExplicitParameterNode`, `ThisParameterNode`, or - * `ParameterIndirectionNode`. - */ -final class ParameterNode = AbstractParameterNode; - -abstract private class AbstractDirectParameterNode extends AbstractParameterNode { } - -/** An explicit positional parameter, including `this`, but not `...`. */ -final class DirectParameterNode = AbstractDirectParameterNode; - -/** - * INTERNAL: Do not use. - * - * A non-indirect parameter node that is represented as an `Instruction`. - */ -abstract class InstructionDirectParameterNode extends InstructionNode, AbstractDirectParameterNode { - final override InitializeParameterInstruction instr; - - /** - * INTERNAL: Do not use. - * - * Gets the `IRVariable` that this parameter references. - */ - final IRVariable getIRVariable() { result = instr.getIRVariable() } -} - -abstract private class AbstractExplicitParameterNode extends AbstractDirectParameterNode { } - -final class ExplicitParameterNode = AbstractExplicitParameterNode; - -/** An explicit positional parameter, not including `this` or `...`. */ -private class ExplicitParameterInstructionNode extends AbstractExplicitParameterNode, - InstructionDirectParameterNode -{ - ExplicitParameterInstructionNode() { exists(instr.getParameter()) } - - override predicate isSourceParameterOf(Function f, ParameterPosition pos) { - f.getParameter(pos.(DirectPosition).getArgumentIndex()) = instr.getParameter() - } - - override string toStringImpl() { result = instr.getParameter().toString() } - - override Parameter getParameter() { result = instr.getParameter() } -} - -/** An implicit `this` parameter. */ -class ThisParameterInstructionNode extends AbstractExplicitParameterNode, - InstructionDirectParameterNode -{ - ThisParameterInstructionNode() { instr.getIRVariable() instanceof IRThisVariable } - - override predicate isSourceParameterOf(Function f, ParameterPosition pos) { - pos.(DirectPosition).getArgumentIndex() = -1 and - instr.getEnclosingFunction() = f - } - - override string toStringImpl() { result = "this" } -} - -/** - * A parameter node that is part of a summary. - */ -class SummaryParameterNode extends AbstractParameterNode, FlowSummaryNode { - SummaryParameterNode() { - FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), _) - } - - private ParameterPosition getPosition() { - FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), result) - } - - override predicate isSummaryParameterOf( - FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition p - ) { - c = this.getSummarizedCallable() and - p = this.getPosition() - } -} - -private class DirectBodyLessParameterNode extends AbstractExplicitParameterNode, - BodyLessParameterNodeImpl -{ - DirectBodyLessParameterNode() { indirectionIndex = 0 } - - override predicate isSourceParameterOf(Function f, ParameterPosition pos) { - this.getFunction() = f and - f.getParameter(pos.(DirectPosition).getArgumentIndex()) = p - } - - override Parameter getParameter() { result = p } -} - -private class IndirectBodyLessParameterNode extends AbstractIndirectParameterNode, - BodyLessParameterNodeImpl -{ - IndirectBodyLessParameterNode() { not this instanceof DirectBodyLessParameterNode } - - override predicate isSourceParameterOf(Function f, ParameterPosition pos) { - exists(int argumentPosition | - this.getFunction() = f and - f.getParameter(argumentPosition) = p and - indirectPositionHasArgumentIndexAndIndex(pos, argumentPosition, indirectionIndex) - ) - } - - override int getIndirectionIndex() { - result = BodyLessParameterNodeImpl.super.getIndirectionIndex() - } - - override Parameter getParameter() { result = p } -} - -/** - * A node associated with an object after an operation that might have - * changed its state. - * - * This can be either the argument to a callable after the callable returns - * (which might have mutated the argument), or the qualifier of a field after - * an update to the field. - * - * Nodes corresponding to AST elements, for example `ExprNode`, usually refer - * to the value before the update with the exception of `ClassInstanceExpr`, - * which represents the value after the constructor has run. - */ -abstract class PostUpdateNode extends Node { - /** - * Gets the node before the state update. - */ - abstract Node getPreUpdateNode(); - - final override Type getType() { result = this.getPreUpdateNode().getType() } -} - -/** - * The base class for nodes that perform "partial definitions". - * - * In contrast to a normal "definition", which provides a new value for - * something, a partial definition is an expression that may affect a - * value, but does not necessarily replace it entirely. For example: - * ``` - * x.y = 1; // a partial definition of the object `x`. - * x.y.z = 1; // a partial definition of the object `x.y` and `x`. - * x.setY(1); // a partial definition of the object `x`. - * setY(&x); // a partial definition of the object `x`. - * ``` - */ -abstract private class PartialDefinitionNode extends PostUpdateNode { - /** Gets the indirection index of this node. */ - abstract int getIndirectionIndex(); - - /** Gets the expression that is partially defined by this node. */ - 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. - * - * A typical example would be a call `f(&x)`. Firstly, there will be flow into - * `x` from previous definitions of `x`. Secondly, there will be a - * `DefinitionByReferenceNode` to represent the value of `x` after the call has - * returned. This node will have its `getArgument()` equal to `&x` and its - * `getVariableAccess()` equal to `x`. - */ -class DefinitionByReferenceNode extends IndirectArgumentOutNode { - DefinitionByReferenceNode() { this.getIndirectionIndex() > 0 } - - /** Gets the unconverted argument corresponding to this node. */ - Expr getArgument() { result = this.getAddressOperand().getDef().getUnconvertedResultExpression() } - - /** Gets the parameter through which this value is assigned. */ - Parameter getParameter() { - result = this.getCallInstruction().getStaticCallTarget().getParameter(this.getArgumentIndex()) - } -} - -/** - * A `Node` corresponding to a global (or `static` local) variable in the - * program, as opposed to the value of that variable at some particular point. - * This is used to model flow through global variables (and `static` local - * variables). - * - * There is no `VariableNode` for non-`static` local variables. - */ -class VariableNode extends Node, TGlobalLikeVariableNode { - Variable v; - int indirectionIndex; - - VariableNode() { this = TGlobalLikeVariableNode(v, indirectionIndex) } - - /** Gets the variable corresponding to this node. */ - Variable getVariable() { result = v } - - /** Gets the indirection index of this node. */ - int getIndirectionIndex() { result = indirectionIndex } - - override Declaration getFunction() { none() } - - override DataFlowCallable getEnclosingCallable() { - // 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. - // Therefore we want a distinct enclosing callable for each `VariableNode`, - // and that can be the `Variable` itself. - result.asSourceCallable() = v - } - - override Type getType() { result = getTypeImpl(v.getUnderlyingType(), indirectionIndex - 1) } - - final override Location getLocationImpl() { - // Certain variables (such as parameters) can have multiple locations. - // When there's a unique location we use that one, but if multiple locations - // exist we default to an unknown location. - result = unique( | | v.getLocation()) - or - not exists(unique( | | v.getLocation())) and - result instanceof UnknownLocation - } - - override string toStringImpl() { result = stars(this) + v.toString() } -} - -/** - * Gets the node corresponding to `instr`. - */ -InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr } - -/** - * Gets the node corresponding to `operand`. - */ -OperandNode operandNode(Operand operand) { result.getOperand() = operand } - -/** - * Gets the `Node` corresponding to the value of evaluating `e` or any of its - * conversions. There is no result if `e` is a `Conversion`. For data flowing - * _out of_ an expression, like when an argument is passed by reference, use - * `definitionByReferenceNodeFromArgument` instead. - */ -ExprNode exprNode(Expr e) { result.getExpr(_) = e } - -/** - * Gets the `Node` corresponding to the value of evaluating `e`. Here, `e` may - * be a `Conversion`. For data flowing _out of_ an expression, like when an - * argument is passed by reference, use - * `definitionByReferenceNodeFromArgument` instead. - */ -ExprNode convertedExprNode(Expr e) { result.getConvertedExpr(_) = e } - -/** - * Gets the `Node` corresponding to the value of `p` at function entry. - */ -ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p } - -/** - * Gets the `Node` corresponding to a definition by reference of the variable - * that is passed as unconverted `argument` of a call. - */ -DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) { - result.getArgument() = argument -} - -/** Gets the `VariableNode` corresponding to the variable `v`. */ -VariableNode variableNode(Variable v) { - result.getVariable() = v and result.getIndirectionIndex() = 1 -} - -/** - * DEPRECATED: See UninitializedNode. - * - * Gets the `Node` corresponding to the value of an uninitialized local - * variable `v`. - */ -Node uninitializedNode(LocalVariable v) { none() } - -predicate hasOperandAndIndex(IndirectOperand indirectOperand, Operand operand, int indirectionIndex) { - indirectOperand.hasOperandAndIndirectionIndex(operand, indirectionIndex) -} - -predicate hasInstructionAndIndex( - IndirectInstruction indirectInstr, Instruction instr, int indirectionIndex -) { - indirectInstr.hasInstructionAndIndirectionIndex(instr, indirectionIndex) -} +private import DataFlowNodes +import DataFlowNodes::Public cached private module Cached { @@ -1818,6 +125,7 @@ private module Cached { cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) { ( + TaintTrackingUtil::forceCachingInSameStage() and // Def-use/Use-use flow SsaImpl::ssaFlow(nodeFrom, nodeTo) or @@ -1919,35 +227,7 @@ private module Cached { ) ) } -} - -import Cached - -/** - * Holds if data flows from `source` to `sink` in zero or more local - * (intra-procedural) steps. - */ -pragma[inline] -predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } - -/** - * Holds if data can flow from `i1` to `i2` in zero or more - * local (intra-procedural) steps. - */ -pragma[inline] -predicate localInstructionFlow(Instruction e1, Instruction e2) { - localFlow(instructionNode(e1), instructionNode(e2)) -} -/** - * INTERNAL: Do not use. - * - * Ideally this module would be private, but the `asExprInternal` predicate is - * needed in `DefaultTaintTrackingImpl`. Once `DefaultTaintTrackingImpl` is gone - * we can make this module private. - */ -cached -module ExprFlowCached { /** * Holds if `n` is an indirect operand of a `PointerArithmeticInstruction`, and * `e` is the result of loading from the `PointerArithmeticInstruction`. @@ -1997,8 +277,7 @@ module ExprFlowCached { * `x[i]` steps to the expression `x[i - 1]` without traversing the * entire chain. */ - cached - Expr asExprInternal(Node n) { + private Expr asExprInternal(Node n) { isIndirectBaseOfArrayAccess(n, result) or not isIndirectBaseOfArrayAccess(n, _) and @@ -2060,7 +339,23 @@ module ExprFlowCached { predicate localExprFlowStep(Expr e1, Expr e2) { localExprFlowStepImpl(_, e1, _, e2) } } -import ExprFlowCached +import Cached + +/** + * Holds if data flows from `source` to `sink` in zero or more local + * (intra-procedural) steps. + */ +pragma[inline] +predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } + +/** + * Holds if data can flow from `i1` to `i2` in zero or more + * local (intra-procedural) steps. + */ +pragma[inline] +predicate localInstructionFlow(Instruction e1, Instruction e2) { + localFlow(instructionNode(e1), instructionNode(e2)) +} /** * Holds if data can flow from `e1` to `e2` in one or more @@ -2080,158 +375,6 @@ predicate localExprFlow(Expr e1, Expr e2) { localExprFlowPlus(e1, e2) } -/** - * A canonical representation of a field. - * - * For performance reasons we want a unique `Content` that represents - * a given field across any template instantiation of a class. - * - * This is possible in _almost_ all cases, but there are cases where it is - * not possible to map between a field in the uninstantiated template to a - * field in the instantiated template. This happens in the case of local class - * definitions (because the local class is not the template that constructs - * the instantiation - it is the enclosing function). So this abstract class - * has two implementations: a non-local case (where we can represent a - * canonical field as the field declaration from an uninstantiated class - * template or a non-templated class), and a local case (where we simply use - * the field from the instantiated class). - */ -abstract private class CanonicalField extends Field { - /** Gets a field represented by this canonical field. */ - abstract Field getAField(); - - /** - * Gets a class that declares a field represented by this canonical field. - */ - abstract Class getADeclaringType(); - - /** - * Gets a type that this canonical field may have. Note that this may - * not be a unique type. For example, consider this case: - * ``` - * template - * struct S { T x; }; - * - * S s1; - * S s2; - * ``` - * In this case the canonical field corresponding to `S::x` has two types: - * `int` and `char`. - */ - Type getAType() { result = this.getAField().getType() } - - Type getAnUnspecifiedType() { result = this.getAType().getUnspecifiedType() } -} - -private class NonLocalCanonicalField extends CanonicalField { - Class declaringType; - - NonLocalCanonicalField() { - declaringType = this.getDeclaringType() and - not declaringType.isFromTemplateInstantiation(_) and - not declaringType.isLocal() // handled in LocalCanonicalField - } - - override Field getAField() { - exists(Class c | result.getDeclaringType() = c | - // Either the declaring class of the field is a template instantiation - // that has been constructed from this canonical declaration - c.isConstructedFrom(declaringType) and - pragma[only_bind_out](result.getName()) = pragma[only_bind_out](this.getName()) - or - // or this canonical declaration is not a template. - not c.isConstructedFrom(_) and - result = this - ) - } - - override Class getADeclaringType() { - result = this.getDeclaringType() - or - result.isConstructedFrom(this.getDeclaringType()) - } -} - -private class LocalCanonicalField extends CanonicalField { - Class declaringType; - - LocalCanonicalField() { - declaringType = this.getDeclaringType() and - declaringType.isLocal() - } - - override Field getAField() { result = this } - - override Class getADeclaringType() { result = declaringType } -} - -/** - * A canonical representation of a `Union`. See `CanonicalField` for the explanation for - * why we need a canonical representation. - */ -abstract private class CanonicalUnion extends Union { - /** Gets a union represented by this canonical union. */ - abstract Union getAUnion(); - - /** Gets a canonical field of this canonical union. */ - CanonicalField getACanonicalField() { result.getDeclaringType() = this } -} - -private class NonLocalCanonicalUnion extends CanonicalUnion { - NonLocalCanonicalUnion() { not this.isFromTemplateInstantiation(_) and not this.isLocal() } - - override Union getAUnion() { - result = this - or - result.isConstructedFrom(this) - } -} - -private class LocalCanonicalUnion extends CanonicalUnion { - LocalCanonicalUnion() { this.isLocal() } - - override Union getAUnion() { result = this } -} - -bindingset[f] -pragma[inline_late] -private int getFieldSize(CanonicalField f) { result = max(f.getAType().getSize()) } - -/** - * Gets a field in the union `u` whose size - * is `bytes` number of bytes. - */ -private CanonicalField getAFieldWithSize(CanonicalUnion u, int bytes) { - result = u.getACanonicalField() and - bytes = getFieldSize(result) -} - -cached -private newtype TContent = - TNonUnionContent(CanonicalField f, int indirectionIndex) { - // the indirection index for field content starts at 1 (because `TNonUnionContent` is thought of as - // the address of the field, `FieldAddress` in the IR). - indirectionIndex = [1 .. max(SsaImpl::getMaxIndirectionsForType(f.getAnUnspecifiedType()))] and - // Reads and writes of union fields are tracked using `UnionContent`. - not f.getDeclaringType() instanceof Union - } or - TUnionContent(CanonicalUnion u, int bytes, int indirectionIndex) { - exists(CanonicalField f | - f = u.getACanonicalField() and - bytes = getFieldSize(f) and - // We key `UnionContent` by the union instead of its fields since a write to one - // field can be read by any read of the union's fields. Again, the indirection index - // is 1-based (because 0 is considered the address). - indirectionIndex = - [1 .. max(SsaImpl::getMaxIndirectionsForType(getAFieldWithSize(u, bytes) - .getAnUnspecifiedType()) - )] - ) - } or - TElementContent(int indirectionIndex) { - indirectionIndex = [1 .. getMaxElementContentIndirectionIndex()] - } - /** * A description of the way data may be stored inside an object. Examples * include instance fields, the contents of a collection object, or the contents diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll index 6d69dd11e80b..2e4c114a522f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll @@ -6,6 +6,7 @@ private import cpp private import semmle.code.cpp.ir.IR private import DataFlowUtil private import DataFlowPrivate +private import DataFlowNodes private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll index f880bee1c1c4..5fd7bb4567b9 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll @@ -6,6 +6,7 @@ private import semmle.code.cpp.ir.IR private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs private import DataFlowUtil +private import DataFlowNodes private import DataFlowPrivate private import SsaImpl as Ssa diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll index 80b440fff221..bbc34a631346 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll @@ -12,6 +12,7 @@ private import semmle.code.cpp.ir.internal.IRCppLanguage private import semmle.code.cpp.ir.dataflow.internal.ModelUtil private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedInitialization private import DataFlowPrivate +private import DataFlowNodes import SsaImplCommon private module SourceVariables { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll index 10ebfdb5be0d..425657a1cf0c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll @@ -4,6 +4,7 @@ import semmle.code.cpp.ir.internal.IRCppLanguage private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects private import DataFlowImplCommon as DataFlowImplCommon private import DataFlowUtil +private import DataFlowNodes private import semmle.code.cpp.models.interfaces.PointerWrapper private import DataFlowPrivate private import TypeFlow @@ -55,26 +56,6 @@ private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) { result.hasType(f.getTypeOfThis(), isGLValue) } -/** - * Gets the C++ type of the instruction `i`. - * - * This is equivalent to `i.getResultLanguageType()` with the exception - * of instructions that directly references a `this` IRVariable. In this - * case, `i.getResultLanguageType()` gives an unknown type, whereas the - * predicate gives the expected type (i.e., a potentially cv-qualified - * type `A*` where `A` is the declaring type of the member function that - * contains `i`). - */ -cached -CppType getResultLanguageType(Instruction i) { - if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable - then - if i.isGLValue() - then result = getThisType(i.getEnclosingFunction(), true) - else result = getThisType(i.getEnclosingFunction(), false) - else result = i.getResultLanguageType() -} - /** * Gets the C++ type of the operand `operand`. * This is equivalent to the type of the operand's defining instruction. @@ -572,6 +553,26 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns cached private module Cached { + /** + * Gets the C++ type of the instruction `i`. + * + * This is equivalent to `i.getResultLanguageType()` with the exception + * of instructions that directly references a `this` IRVariable. In this + * case, `i.getResultLanguageType()` gives an unknown type, whereas the + * predicate gives the expected type (i.e., a potentially cv-qualified + * type `A*` where `A` is the declaring type of the member function that + * contains `i`). + */ + cached + CppType getResultLanguageType(Instruction i) { + if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable + then + if i.isGLValue() + then result = getThisType(i.getEnclosingFunction(), true) + else result = getThisType(i.getEnclosingFunction(), false) + else result = i.getResultLanguageType() + } + /** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */ private predicate isConversion(Operand op) { exists(Instruction def, Operand use | diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll index f190569330f5..7cb67caa77cc 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll @@ -5,65 +5,77 @@ private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.models.interfaces.SideEffect private import DataFlowUtil private import DataFlowPrivate +private import DataFlowNodes private import SsaImpl as Ssa private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import semmle.code.cpp.ir.dataflow.FlowSteps -/** - * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local - * (intra-procedural) step. This relation is only used for local taint flow - * (for example `TaintTracking::localTaint(source, sink)`) so it may contain - * special cases that should only apply to local taint flow. - */ -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, _) -} - -/** - * Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding - * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent - * different objects. - */ cached -predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) { - operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and - model = "" - or - modeledTaintStep(nodeFrom, nodeTo, model) - or - // Flow from (the indirection of) an operand of a pointer arithmetic instruction to the - // indirection of the pointer arithmetic instruction. This provides flow from `source` - // in `x[source]` to the result of the associated load instruction. - exists(PointerArithmeticInstruction pai, int indirectionIndex | - nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and - hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1) - ) and - model = "" - or - any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and - model = "" - or - // models-as-data summarized flow - FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), - nodeTo.(FlowSummaryNode).getSummaryNode(), false, model) - or - // object->field conflation for content that is a `TaintInheritingContent`. - exists(DataFlow::ContentSet f | - readStep(nodeFrom, f, nodeTo) and - f.getAReadContent() instanceof TaintInheritingContent - ) and - model = "" +private module Cached { + private import DataFlowImplCommon as DataFlowImplCommon + + cached + predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() } + + /** + * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. This relation is only used for local taint flow + * (for example `TaintTracking::localTaint(source, sink)`) so it may contain + * special cases that should only apply to local taint flow. + */ + cached + 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, _) + } + + /** + * Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding + * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent + * different objects. + */ + cached + predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) { + operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and + model = "" + or + modeledTaintStep(nodeFrom, nodeTo, model) + or + // Flow from (the indirection of) an operand of a pointer arithmetic instruction to the + // indirection of the pointer arithmetic instruction. This provides flow from `source` + // in `x[source]` to the result of the associated load instruction. + exists(PointerArithmeticInstruction pai, int indirectionIndex | + nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and + hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1) + ) and + model = "" + or + any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and + model = "" + or + // models-as-data summarized flow + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), + nodeTo.(FlowSummaryNode).getSummaryNode(), false, model) + or + // object->field conflation for content that is a `TaintInheritingContent`. + exists(DataFlow::ContentSet f | + readStep(nodeFrom, f, nodeTo) and + f.getAReadContent() instanceof TaintInheritingContent + ) and + model = "" + } } +import Cached + /** * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. @@ -196,7 +208,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string // Taint flow from a pointer argument to an output, when the model specifies flow from the deref // to that output, but the deref is not modeled in the IR for the caller. exists( - CallInstruction call, DataFlow::SideEffectOperandNode indirectArgument, Function func, + CallInstruction call, SideEffectOperandNode indirectArgument, Function func, FunctionInput modelIn, FunctionOutput modelOut | indirectArgument = callInput(call, modelIn) and diff --git a/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql b/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql index b4e517b3bab9..c85b33a9727a 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql @@ -15,6 +15,7 @@ import cpp import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes /** Gets a loop that contains `e`. */ Loop getAnEnclosingLoopOfExpr(Expr e) { result = getAnEnclosingLoopOfStmt(e.getEnclosingStmt()) } @@ -45,9 +46,9 @@ private Expr getExpr(DataFlow::Node node) { or result = node.asOperand().getUse().getAst() or - result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst() + result = node.(RawIndirectInstruction).getInstruction().getAst() or - result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst() + result = node.(RawIndirectOperand).getOperand().getUse().getAst() } /** @@ -208,7 +209,7 @@ class LoopWithAlloca extends Stmt { this.conditionRequiresInequality(va, _, _) and DataFlow::localFlow(result, DataFlow::exprNode(va)) and // Phi nodes will be preceded by nodes that represent actual definitions - not result instanceof DataFlow::SsaSynthNode and + not result instanceof SsaSynthNode and // A source is outside the loop if it's not inside the loop not exists(Expr e | e = getExpr(result) | this = getAnEnclosingLoopOfExpr(e)) ) diff --git a/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll index f2c621d04cb6..90160df3c210 100644 --- a/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll +++ b/cpp/ql/src/utils/modelgenerator/internal/CaptureModels.qll @@ -8,6 +8,7 @@ private import semmle.code.cpp.dataflow.ExternalFlow as ExternalFlow private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as DataFlowPrivate +private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes as DataFlowNodes private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific private import semmle.code.cpp.dataflow.new.TaintTracking as Tt @@ -403,7 +404,7 @@ private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig { } predicate apiSource(DataFlow::Node source) { - DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1) + DataFlowPrivate::nodeHasOperand(source, any(DataFlowNodes::FieldAddress fa), 1) or source instanceof DataFlow::ParameterNode } @@ -416,7 +417,7 @@ private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig { result = "Argument[" + DataFlow::repeatStars(indirectionIndex) + argumentIndex + "]" ) or - DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1) and + DataFlowPrivate::nodeHasOperand(source, any(DataFlowNodes::FieldAddress fa), 1) and result = qualifierString() } diff --git a/cpp/ql/test/library-tests/dataflow/models-as-data/FlowSummaryNode.ql b/cpp/ql/test/library-tests/dataflow/models-as-data/FlowSummaryNode.ql index 399e2e129b3b..7b551515b460 100644 --- a/cpp/ql/test/library-tests/dataflow/models-as-data/FlowSummaryNode.ql +++ b/cpp/ql/test/library-tests/dataflow/models-as-data/FlowSummaryNode.ql @@ -1,6 +1,7 @@ import testModels 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.DataFlowNodes string describe(DataFlow::Node n) { n instanceof ParameterNode and result = "ParameterNode" From a0917aeb5b8b3a254720908329fc2ff7c929cf41 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 26 Feb 2026 13:02:42 +0000 Subject: [PATCH 2/4] C++: Remove IR re-evaluation. --- .../cpp/ir/dataflow/internal/ExprNodes.qll | 42 +++------ .../code/cpp/ir/dataflow/internal/SsaImpl.qll | 7 +- .../ir/dataflow/internal/SsaImplCommon.qll | 83 ++++++++--------- .../raw/internal/IRConstruction.qll | 90 +++++++++++++++++++ 4 files changed, 145 insertions(+), 77 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll index 2e4c114a522f..927d2ea90288 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll @@ -7,8 +7,7 @@ private import semmle.code.cpp.ir.IR private import DataFlowUtil private import DataFlowPrivate private import DataFlowNodes -private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr -private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag +private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction cached private module Cached { @@ -74,17 +73,9 @@ private module Cached { // a result for `getConvertedResultExpression`. We remap this here so that // this `ConvertInstruction` maps to the result of the expression that // represents the extent. - exists(TranslatedNonConstantAllocationSize tas | - result = tas.getExtent().getExpr() and - instr = tas.getInstruction(AllocationExtentConvertTag()) - ) + result = IRConstruction::Raw::getAllocationExtentConvertExpr(instr) or - // There's no instruction that returns `ParenthesisExpr`, but some queries - // expect this - exists(TranslatedTransparentConversion ttc | - result = ttc.getExpr().(ParenthesisExpr) and - instr = ttc.getResult() - ) + result = IRConstruction::Raw::getTransparentConversionParenthesisExpr(instr) or // Certain expressions generate `CopyValueInstruction`s only when they // are needed. Examples of this include crement operations and compound @@ -113,10 +104,10 @@ private module Cached { // needed, and in that case the only value that will propagate forward in // the program is the value that's been updated. So in those cases we just // use the result of `node.asDefinition()` as the result of `node.asExpr()`. - exists(TranslatedCoreExpr tco | - tco.getInstruction(_) = instr and - tco.producesExprResult() and - result = asDefinitionImpl0(instr) + exists(StoreInstruction store | + store = instr and + IRConstruction::Raw::instructionProducesExprResult(store) and + result = asDefinitionImpl0(store) ) or // IR construction breaks an array aggregate literal `{1, 2, 3}` into a @@ -146,18 +137,9 @@ private module Cached { // For an expression such as `i += 2` we pretend that the generated // `StoreInstruction` contains the result of the expression even though // this isn't totally aligned with the C/C++ standard. - exists(TranslatedAssignOperation tao | - store = tao.getInstruction(AssignmentStoreTag()) and - result = tao.getExpr() - ) + result = IRConstruction::Raw::getAssignOperationStoreExpr(store) or - // Similarly for `i++` and `++i` we pretend that the generated - // `StoreInstruction` contains the result of the expression even though - // this isn't totally aligned with the C/C++ standard. - exists(TranslatedCrementOperation tco | - store = tco.getInstruction(CrementStoreTag()) and - result = tco.getExpr() - ) + result = IRConstruction::Raw::getCrementOperationStoreExpr(store) } /** @@ -167,11 +149,7 @@ private module Cached { */ private predicate excludeAsDefinitionResult(StoreInstruction store) { // Exclude the store to the temporary generated by a ternary expression. - exists(TranslatedConditionalExpr tce | - store = tce.getInstruction(ConditionValueFalseStoreTag()) - or - store = tce.getInstruction(ConditionValueTrueStoreTag()) - ) + IRConstruction::Raw::isConditionalExprTempStore(store) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll index bbc34a631346..f1bdd6b8c520 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll @@ -10,7 +10,7 @@ private import semmle.code.cpp.models.interfaces.PartialFlow as PartialFlow private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as FIO private import semmle.code.cpp.ir.internal.IRCppLanguage private import semmle.code.cpp.ir.dataflow.internal.ModelUtil -private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedInitialization +private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction private import DataFlowPrivate private import DataFlowNodes import SsaImplCommon @@ -439,10 +439,7 @@ private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVari * initialize `v`. */ private Instruction getInitializationTargetAddress(IRVariable v) { - exists(TranslatedVariableInitialization init | - init.getIRVariable() = v and - result = init.getTargetAddress() - ) + result = IRConstruction::Raw::getInitializationTargetAddress(v) } /** An initial definition of an SSA variable address. */ diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll index 425657a1cf0c..45a6755356b5 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll @@ -10,42 +10,6 @@ private import DataFlowPrivate private import TypeFlow private import semmle.code.cpp.ir.ValueNumbering -/** - * Holds if `operand` is an operand that is not used by the dataflow library. - * Ignored operands are not recognized as uses by SSA, and they don't have a - * corresponding `(Indirect)OperandNode`. - */ -predicate ignoreOperand(Operand operand) { - operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or - operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or - operand instanceof MemoryOperand -} - -/** - * Holds if `instr` is an instruction that is not used by the dataflow library. - * Ignored instructions are not recognized as reads/writes by SSA, and they - * don't have a corresponding `(Indirect)InstructionNode`. - */ -predicate ignoreInstruction(Instruction instr) { - DataFlowImplCommon::forceCachingInSameStage() and - ( - instr instanceof CallSideEffectInstruction or - instr instanceof CallReadSideEffectInstruction or - instr instanceof ExitFunctionInstruction or - instr instanceof EnterFunctionInstruction or - instr instanceof WriteSideEffectInstruction or - instr instanceof PhiInstruction or - instr instanceof ReadSideEffectInstruction or - instr instanceof ChiInstruction or - instr instanceof InitializeIndirectionInstruction or - instr instanceof AliasedDefinitionInstruction or - instr instanceof AliasedUseInstruction or - instr instanceof InitializeNonLocalInstruction or - instr instanceof ReturnIndirectionInstruction or - instr instanceof UninitializedGroupInstruction - ) -} - /** * Gets the C++ type of `this` in the member function `f`. * The result is a glvalue if `isGLValue` is true, and @@ -328,10 +292,6 @@ predicate isWrite(Node0Impl value, Operand address, boolean certain) { ) } -predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) { - any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo) -} - newtype TBaseSourceVariable = // Each IR variable gets its own source variable TBaseIRVariable(IRVariable var) or @@ -553,6 +513,49 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns cached private module Cached { + /** + * Holds if `operand` is an operand that is not used by the dataflow library. + * Ignored operands are not recognized as uses by SSA, and they don't have a + * corresponding `(Indirect)OperandNode`. + */ + cached + predicate ignoreOperand(Operand operand) { + operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or + operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or + operand instanceof MemoryOperand + } + + /** + * Holds if `instr` is an instruction that is not used by the dataflow library. + * Ignored instructions are not recognized as reads/writes by SSA, and they + * don't have a corresponding `(Indirect)InstructionNode`. + */ + cached + predicate ignoreInstruction(Instruction instr) { + DataFlowImplCommon::forceCachingInSameStage() and + ( + instr instanceof CallSideEffectInstruction or + instr instanceof CallReadSideEffectInstruction or + instr instanceof ExitFunctionInstruction or + instr instanceof EnterFunctionInstruction or + instr instanceof WriteSideEffectInstruction or + instr instanceof PhiInstruction or + instr instanceof ReadSideEffectInstruction or + instr instanceof ChiInstruction or + instr instanceof InitializeIndirectionInstruction or + instr instanceof AliasedDefinitionInstruction or + instr instanceof AliasedUseInstruction or + instr instanceof InitializeNonLocalInstruction or + instr instanceof ReturnIndirectionInstruction or + instr instanceof UninitializedGroupInstruction + ) + } + + cached + predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) { + any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo) + } + /** * Gets the C++ type of the instruction `i`. * diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 594e37b668d6..503ce36b3f83 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -15,6 +15,7 @@ private import TranslatedCall private import TranslatedStmt private import TranslatedFunction private import TranslatedGlobalVar +private import TranslatedInitialization TranslatedElement getInstructionTranslatedElement(Instruction instruction) { instruction = TRawInstruction(result, _) @@ -194,6 +195,95 @@ module Raw { Expr getInstructionUnconvertedResultExpression(Instruction instruction) { result = getInstructionConvertedResultExpression(instruction).getUnconverted() } + + /** + * Gets the expression associated with an allocation extent conversion instruction. + * This maps the intermediate `ConvertInstruction` for `new[]` extents back to + * the extent expression. + */ + cached + Expr getAllocationExtentConvertExpr(Instruction instr) { + exists(TranslatedNonConstantAllocationSize tas | + instr = tas.getInstruction(AllocationExtentConvertTag()) and + result = tas.getExtent().getExpr() + ) + } + + /** + * Gets the `ParenthesisExpr` associated with a transparent conversion instruction, + * if any. + */ + cached + ParenthesisExpr getTransparentConversionParenthesisExpr(Instruction instr) { + exists(TranslatedTransparentConversion ttc | + result = ttc.getExpr() and + instr = ttc.getResult() + ) + } + + /** + * Holds if `instr` belongs to a `TranslatedCoreExpr` that produces an + * expression result. This indicates that the instruction represents a + * definition whose result should be mapped back to the expression. + */ + cached + predicate instructionProducesExprResult(Instruction instr) { + exists(TranslatedCoreExpr tco | + tco.getInstruction(_) = instr and + tco.producesExprResult() + ) + } + + /** + * Gets the expression for an assignment operation store instruction. + * Maps `StoreInstruction`s generated by compound assignment operations + * (e.g., `x += 2`) back to the assignment expression. + */ + cached + Expr getAssignOperationStoreExpr(StoreInstruction store) { + exists(TranslatedAssignOperation tao | + store = tao.getInstruction(AssignmentStoreTag()) and + result = tao.getExpr() + ) + } + + /** + * Gets the expression for a crement operation store instruction. + * Maps `StoreInstruction`s generated by crement operations + * (e.g., `x++`, `++x`) back to the crement expression. + */ + cached + Expr getCrementOperationStoreExpr(StoreInstruction store) { + exists(TranslatedCrementOperation tco | + store = tco.getInstruction(CrementStoreTag()) and + result = tco.getExpr() + ) + } + + /** + * Holds if `store` is a temporary store generated by a conditional (ternary) + * expression. Such stores should be excluded from `asDefinition()` results. + */ + cached + predicate isConditionalExprTempStore(StoreInstruction store) { + exists(TranslatedConditionalExpr tce | + store = tce.getInstruction(ConditionValueFalseStoreTag()) + or + store = tce.getInstruction(ConditionValueTrueStoreTag()) + ) + } + + /** + * Gets the instruction that computes the address used to initialize + * the IR variable `v`. + */ + cached + Instruction getInitializationTargetAddress(IRVariable v) { + exists(TranslatedVariableInitialization init | + init.getIRVariable() = v and + result = init.getTargetAddress() + ) + } } class TStageInstruction = TRawInstruction or TRawUnreachedInstruction; From 5cb77f265d74b21867260237f9624f24e0ebbe16 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 26 Feb 2026 14:35:59 +0000 Subject: [PATCH 3/4] C++: Disable magic to prevent re-evaluation. --- cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index cf8f01e69442..1ffe2a41a6b0 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -555,6 +555,7 @@ private Locatable getSupportedFunctionTemplateArgument(Function templateFunction * Normalize the `n`'th parameter of `f` by replacing template names * with `func:N` (where `N` is the index of the template). */ +pragma[nomagic] private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) { exists(Function templateFunction | templateFunction = getFullyTemplatedFunction(f) and From 1d06afaffd42ccc36a91d540a193110df206a35b Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 26 Feb 2026 15:06:07 +0000 Subject: [PATCH 4/4] C++: Remove unnecessary recursion through Node.toString. --- .../code/cpp/dataflow/internal/FlowSummaryImpl.qll | 2 +- .../code/cpp/ir/dataflow/internal/DataFlowNodes.qll | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll index af3e25ba7344..cce1b80e7fcb 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -201,7 +201,7 @@ module SourceSinkInterpretationInput implements string toString() { result = this.asElement().toString() or - result = this.asNode().toString() + result = this.asNode().toStringImpl() or result = this.asCall().toString() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll index fc94d3bfa750..84e95ec4a435 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll @@ -975,7 +975,7 @@ private class Node0 extends Node, TNode0 { override Location getLocationImpl() { result = node.getLocation() } - override string toStringImpl() { result = node.toString() } + override string toStringImpl() { result = node.toStringImpl() } override Type getType() { result = node.getType() } @@ -1027,7 +1027,9 @@ class PostFieldUpdateNode extends PostUpdateNodeImpl { Field getUpdatedField() { result = this.getFieldAddress().getField() } - override string toStringImpl() { result = this.getPreUpdateNode() + " [post update]" } + override string toStringImpl() { + result = this.getPreUpdateNode().toStringImpl() + " [post update]" + } } /** @@ -1195,9 +1197,7 @@ private module RawIndirectNodes { else result instanceof UnknownLocation } - override string toStringImpl() { - result = stars(this) + operandNode(this.getOperand()).toStringImpl() - } + override string toStringImpl() { result = stars(this) + operandToString(this.getOperand()) } } /** @@ -1240,7 +1240,7 @@ private module RawIndirectNodes { } override string toStringImpl() { - result = stars(this) + instructionNode(this.getInstruction()).toStringImpl() + result = stars(this) + instructionToString(this.getInstruction()) } }