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 4351ef90187..ffe045e265f 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 @@ -946,3 +946,859 @@ module Public { } 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 } +} + +/** + * 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 } +} + +/** + * 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/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index e01089d54cd..41a9310fe78 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 @@ -21,1148 +21,6 @@ private import DataFlowDispatch as DataFlowDispatch private import DataFlowNodes import DataFlowNodes::Public -/** - * 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) -} - cached private module Cached { /**