diff --git a/cpp/ql/lib/change-notes/2025-07-31-ssa.md b/cpp/ql/lib/change-notes/2025-07-31-ssa.md new file mode 100644 index 000000000000..1a0a5813d7b4 --- /dev/null +++ b/cpp/ql/lib/change-notes/2025-07-31-ssa.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Exposed various SSA-related classes (`Definition`, `PhiNode`, `ExplicitDefinition`, `DirectExplicitDefinition`, and `IndirectExplicitDefinition`) which were previously only usable inside the internal dataflow directory. \ No newline at end of file diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll index 1e0b39be1ac7..9c6069d4a0c5 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll @@ -15,6 +15,13 @@ class StandardSsa extends SsaHelper { } /** + * NOTE: If possible, prefer the SSA classes exposed by the new dataflow + * library: + * ``` + * import semmle.code.cpp.dataflow.new.DataFlow + * // use `DataFlow::Ssa::Definition` + * ``` + * * A definition of one or more SSA variables, including phi node definitions. * An _SSA variable_, as defined in the literature, is effectively the pair of * an `SsaDefinition d` and a `StackVariable v`, written `(d, v)` in this diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll index 1a4c777af35b..b5e899bf0aac 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll @@ -1,6 +1,5 @@ private import cpp private import semmle.code.cpp.ir.IR -private import semmle.code.cpp.ir.dataflow.DataFlow private import DataFlowPrivate private import DataFlowUtil private import DataFlowImplCommon as DataFlowImplCommon @@ -60,7 +59,7 @@ private module VirtualDispatch { * `resolve` predicate to stitch that information together and resolve the * call. */ - abstract DataFlow::Node getDispatchValue(); + abstract Node getDispatchValue(); /** Gets a candidate target for this call. */ abstract Function resolve(); @@ -72,17 +71,13 @@ private module VirtualDispatch { * parameter is true when the search is allowed to continue backwards into * a parameter; non-recursive callers should pass `_` for `allowFromArg`. */ - predicate flowsFrom(DataFlow::Node src, boolean allowFromArg) { + predicate flowsFrom(Node src, boolean allowFromArg) { src = this.getDispatchValue() and allowFromArg = true or - exists(DataFlow::Node other, boolean allowOtherFromArg | - this.flowsFrom(other, allowOtherFromArg) - | + exists(Node other, boolean allowOtherFromArg | this.flowsFrom(other, allowOtherFromArg) | // Call argument exists(DataFlowCall call, Position i | - other - .(DataFlow::ParameterNode) - .isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and + other.(ParameterNode).isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and src.(ArgumentNode).argumentOf(call, pragma[only_bind_into](pragma[only_bind_out](i))) ) and allowOtherFromArg = true and @@ -96,7 +91,7 @@ private module VirtualDispatch { allowFromArg = false or // Local flow - DataFlow::localFlowStep(src, other) and + localFlowStep(src, other) and allowFromArg = allowOtherFromArg or // Flow from global variable to load. @@ -159,11 +154,11 @@ private module VirtualDispatch { private class DataSensitiveExprCall extends DataSensitiveCall { DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) } - override DataFlow::Node getDispatchValue() { result.asOperand() = this.getCallTargetOperand() } + override Node getDispatchValue() { result.asOperand() = this.getCallTargetOperand() } override Function resolve() { exists(FunctionInstruction fi | - this.flowsFrom(DataFlow::instructionNode(fi), _) and + this.flowsFrom(instructionNode(fi), _) and result = fi.getFunctionSymbol() ) and ( @@ -186,7 +181,7 @@ private module VirtualDispatch { ) } - override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getArgument(-1) } + override Node getDispatchValue() { result.asInstruction() = this.getArgument(-1) } override MemberFunction resolve() { exists(Class overridingClass | @@ -213,7 +208,7 @@ private module VirtualDispatch { pragma[noinline] private predicate hasFlowFromCastFrom(Class derivedClass) { exists(ConvertToBaseInstruction toBase | - this.flowsFrom(DataFlow::instructionNode(toBase), _) and + this.flowsFrom(instructionNode(toBase), _) and derivedClass = toBase.getDerivedClass() ) } @@ -270,7 +265,7 @@ private predicate mayBenefitFromCallContext( exists(InitializeParameterInstruction init | not exists(call.getStaticCallTarget()) and init.getEnclosingFunction() = f.getUnderlyingCallable() and - call.flowsFrom(DataFlow::instructionNode(init), _) and + call.flowsFrom(instructionNode(init), _) and init.getParameter().getIndex() = arg ) } 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 192d00b5942e..f308ee190e7a 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 @@ -4,7 +4,7 @@ private import semmle.code.cpp.ir.IR private import DataFlowDispatch private import semmle.code.cpp.ir.internal.IRCppLanguage private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl -private import SsaInternals as Ssa +private import SsaImpl as Ssa private import DataFlowImplCommon as DataFlowImplCommon private import codeql.util.Unit private import Node0ToString 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 38a4d827a4da..bc3dda5fd345 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 @@ -13,7 +13,7 @@ private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import DataFlowPrivate private import ModelUtil -private import SsaInternals as Ssa +private import SsaImpl as SsaImpl private import DataFlowImplCommon as DataFlowImplCommon private import codeql.util.Unit private import Node0ToString @@ -39,38 +39,39 @@ private newtype TIRDataFlowNode = TNode0(Node0Impl node) { DataFlowImplCommon::forceCachingInSameStage() } or TGlobalLikeVariableNode(GlobalLikeVariable var, int indirectionIndex) { indirectionIndex = - [getMinIndirectionsForType(var.getUnspecifiedType()) .. Ssa::getMaxIndirectionsForType(var.getUnspecifiedType())] + [getMinIndirectionsForType(var.getUnspecifiedType()) .. SsaImpl::getMaxIndirectionsForType(var.getUnspecifiedType())] } or TPostUpdateNodeImpl(Operand operand, int indirectionIndex) { operand = any(FieldAddress fa).getObjectAddressOperand() and - indirectionIndex = [0 .. Ssa::countIndirectionsForCppType(Ssa::getLanguageType(operand))] + indirectionIndex = + [0 .. SsaImpl::countIndirectionsForCppType(SsaImpl::getLanguageType(operand))] or - Ssa::isModifiableByCall(operand, indirectionIndex) + SsaImpl::isModifiableByCall(operand, indirectionIndex) } or - TSsaSynthNode(Ssa::SynthNode n) or + TSsaSynthNode(SsaImpl::SynthNode n) or TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or TRawIndirectOperand0(Node0Impl node, int indirectionIndex) { - Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex) + SsaImpl::hasRawIndirectOperand(node.asOperand(), indirectionIndex) } or TRawIndirectInstruction0(Node0Impl node, int indirectionIndex) { not exists(node.asOperand()) and - Ssa::hasRawIndirectInstruction(node.asInstruction(), indirectionIndex) + SsaImpl::hasRawIndirectInstruction(node.asInstruction(), indirectionIndex) } or TFinalParameterNode(Parameter p, int indirectionIndex) { - exists(Ssa::FinalParameterUse use | + exists(SsaImpl::FinalParameterUse use | use.getParameter() = p and use.getIndirectionIndex() = indirectionIndex ) } or - TFinalGlobalValue(Ssa::GlobalUse globalUse) or - TInitialGlobalValue(Ssa::GlobalDef globalUse) 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 .. Ssa::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1] and + indirectionIndex = [0 .. SsaImpl::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1] and not any(InitializeParameterInstruction init).getParameter() = p } or TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) @@ -81,7 +82,7 @@ private newtype TIRDataFlowNode = class FieldAddress extends Operand { FieldAddressInstruction fai; - FieldAddress() { fai = this.getDef() and not Ssa::ignoreOperand(this) } + FieldAddress() { fai = this.getDef() and not SsaImpl::ignoreOperand(this) } /** Gets the field associated with this instruction. */ Field getField() { result = fai.getField() } @@ -126,7 +127,7 @@ predicate conversionFlow( ) or additional = true and - Ssa::isAdditionalConversionFlow(opFrom, instrTo) + SsaImpl::isAdditionalConversionFlow(opFrom, instrTo) ) or isPointerArith = true and @@ -183,7 +184,7 @@ class Node extends TIRDataFlowNode { or this.asOperand().getUse() = block.getInstruction(i) or - exists(Ssa::SynthNode ssaNode | + exists(SsaImpl::SynthNode ssaNode | this.(SsaSynthNode).getSynthNode() = ssaNode and ssaNode.getBasicBlock() = block and ssaNode.getIndex() = i @@ -364,10 +365,10 @@ class Node extends TIRDataFlowNode { * pointed to by `p`. */ Expr asDefinition(boolean uncertain) { - exists(StoreInstruction store, Ssa::Definition def | + exists(StoreInstruction store, SsaImpl::Definition def | store = this.asInstruction() and result = asDefinitionImpl(store) and - Ssa::defToNode(this, def, _) and + SsaImpl::defToNode(this, def, _) and if def.isCertain() then uncertain = false else uncertain = true ) } @@ -627,7 +628,7 @@ class OperandNode extends Node, Node0 { * For example, `stripPointers(int*&)` is `int*` and `stripPointers(int*)` is `int`. */ Type stripPointer(Type t) { - result = any(Ssa::Indirection ind | ind.getType() = t).getBaseType() + result = any(SsaImpl::Indirection ind | ind.getType() = t).getBaseType() or result = t.(PointerToMemberType).getBaseType() or @@ -694,12 +695,12 @@ class PostFieldUpdateNode extends PostUpdateNodeImpl { * in a data flow graph. */ class SsaSynthNode extends Node, TSsaSynthNode { - Ssa::SynthNode node; + SsaImpl::SynthNode node; SsaSynthNode() { this = TSsaSynthNode(node) } /** Gets the synthesized SSA node associated with this node. */ - Ssa::SynthNode getSynthNode() { result = node } + SsaImpl::SynthNode getSynthNode() { result = node } override DataFlowCallable getEnclosingCallable() { result.asSourceCallable() = this.getFunction() @@ -782,12 +783,12 @@ class SideEffectOperandNode extends Node instanceof IndirectOperand { * from a function body. */ class FinalGlobalValue extends Node, TFinalGlobalValue { - Ssa::GlobalUse globalUse; + SsaImpl::GlobalUse globalUse; FinalGlobalValue() { this = TFinalGlobalValue(globalUse) } /** Gets the underlying SSA use. */ - Ssa::GlobalUse getGlobalUse() { result = globalUse } + SsaImpl::GlobalUse getGlobalUse() { result = globalUse } override DataFlowCallable getEnclosingCallable() { result.asSourceCallable() = this.getFunction() @@ -814,12 +815,12 @@ class FinalGlobalValue extends Node, TFinalGlobalValue { * a function body. */ class InitialGlobalValue extends Node, TInitialGlobalValue { - Ssa::GlobalDef globalDef; + SsaImpl::GlobalDef globalDef; InitialGlobalValue() { this = TInitialGlobalValue(globalDef) } /** Gets the underlying SSA definition. */ - Ssa::GlobalDef getGlobalDef() { result = globalDef } + SsaImpl::GlobalDef getGlobalDef() { result = globalDef } override DataFlowCallable getEnclosingCallable() { result.asSourceCallable() = this.getFunction() @@ -1288,11 +1289,11 @@ class UninitializedNode extends Node { LocalVariable v; UninitializedNode() { - exists(Ssa::Definition def, Ssa::SourceVariable sv | + exists(SsaImpl::Definition def, SsaImpl::SourceVariable sv | def.getIndirectionIndex() = 0 and def.getValue().asInstruction() instanceof UninitializedInstruction and - Ssa::defToNode(this, def, sv) and - v = sv.getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst() + SsaImpl::defToNode(this, def, sv) and + v = sv.getBaseVariable().(SsaImpl::BaseIRVariable).getIRVariable().getAst() ) } @@ -1722,7 +1723,7 @@ private module Cached { cached predicate flowsToBackEdge(Node n) { exists(Node succ, IRBlock bb1, IRBlock bb2 | - Ssa::ssaFlow(n, succ) and + SsaImpl::ssaFlow(n, succ) and bb1 = n.getBasicBlock() and bb2 = succ.getBasicBlock() and bb1 != bb2 and @@ -1820,7 +1821,7 @@ private module Cached { predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) { ( // Def-use/Use-use flow - Ssa::ssaFlow(nodeFrom, nodeTo) + SsaImpl::ssaFlow(nodeFrom, nodeTo) or IteratorFlow::localFlowStep(nodeFrom, nodeTo) or @@ -1833,7 +1834,7 @@ private module Cached { | simpleOperandLocalFlowStep(iFrom, opTo) and // Omit when the instruction node also represents the operand. - not iFrom = Ssa::getIRRepresentationOfOperand(opTo) + not iFrom = SsaImpl::getIRRepresentationOfOperand(opTo) ) or // Indirect operand -> (indirect) instruction flow @@ -1906,7 +1907,7 @@ private module Cached { // We also want a write coming out of an `OutNode` to flow `nodeTo`. // This is different from `reverseFlowInstruction` since `nodeFrom` can never // be an `OutNode` when it's defined by an instruction. - Ssa::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex) + SsaImpl::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex) ) } @@ -2099,7 +2100,7 @@ private newtype TContent = TFieldContent(Field f, int indirectionIndex) { // the indirection index for field content starts at 1 (because `TFieldContent` is thought of as // the address of the field, `FieldAddress` in the IR). - indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(f.getUnspecifiedType())] and + indirectionIndex = [1 .. SsaImpl::getMaxIndirectionsForType(f.getUnspecifiedType())] and // Reads and writes of union fields are tracked using `UnionContent`. not f.getDeclaringType() instanceof Union } or @@ -2111,7 +2112,9 @@ private newtype TContent = // field can be read by any read of the union's fields. Again, the indirection index // is 1-based (because 0 is considered the address). indirectionIndex = - [1 .. max(Ssa::getMaxIndirectionsForType(getAFieldWithSize(u, bytes).getUnspecifiedType()))] + [1 .. max(SsaImpl::getMaxIndirectionsForType(getAFieldWithSize(u, bytes) + .getUnspecifiedType()) + )] ) } or TElementContent(int indirectionIndex) { @@ -2354,7 +2357,7 @@ module BarrierGuard { controls(g, result, edge) ) or - result = Ssa::BarrierGuard::getABarrierNode() + result = SsaImpl::BarrierGuard::getABarrierNode() } /** @@ -2453,7 +2456,7 @@ module BarrierGuard { ) or result = - Ssa::BarrierGuardWithIntParam::getABarrierNode(indirectionIndex) + SsaImpl::BarrierGuardWithIntParam::getABarrierNode(indirectionIndex) } } @@ -2490,7 +2493,7 @@ module InstructionBarrierGuard::getABarrierNode() + result = SsaImpl::BarrierGuard::getABarrierNode() } bindingset[value, n] @@ -2520,7 +2523,7 @@ module InstructionBarrierGuard::getABarrierNode(indirectionIndex) + SsaImpl::BarrierGuardWithIntParam::getABarrierNode(indirectionIndex) } } @@ -2576,3 +2579,16 @@ Function getARuntimeTarget(Call call) { result = DataFlowImplCommon::viableCallableLambda(dfCall, _).asSourceCallable() ) } + +/** A module that provides static single assignment (SSA) information. */ +module Ssa { + class Definition = SsaImpl::Definition; + + class ExplicitDefinition = SsaImpl::ExplicitDefinition; + + class DirectExplicitDefinition = SsaImpl::DirectExplicitDefinition; + + class IndirectExplicitDefinition = SsaImpl::IndirectExplicitDefinition; + + class PhiNode = SsaImpl::PhiNode; +} 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 055f48c80ec8..f880bee1c1c4 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 @@ -4,15 +4,15 @@ */ private import semmle.code.cpp.ir.IR -private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs private import DataFlowUtil private import DataFlowPrivate -private import SsaInternals as Ssa +private import SsaImpl as Ssa /** * Gets the instruction that goes into `input` for `call`. */ -DataFlow::Node callInput(CallInstruction call, FunctionInput input) { +Node callInput(CallInstruction call, FunctionInput input) { // An argument or qualifier exists(int index | result.asOperand() = call.getArgumentOperand(index) and @@ -62,8 +62,8 @@ Node callOutput(CallInstruction call, FunctionOutput output) { result = callOutputWithIndirectionIndex(call, output, _) } -DataFlow::Node callInput(CallInstruction call, FunctionInput input, int d) { - exists(DataFlow::Node n | n = callInput(call, input) and d > 0 | +Node callInput(CallInstruction call, FunctionInput input, int d) { + exists(Node n | n = callInput(call, input) and d > 0 | // An argument or qualifier hasOperandAndIndex(result, n.asOperand(), d) or @@ -85,7 +85,7 @@ private IndirectReturnOutNode getIndirectReturnOutNode(CallInstruction call, int */ bindingset[d] Node callOutput(CallInstruction call, FunctionOutput output, int d) { - exists(DataFlow::Node n, int indirectionIndex | + exists(Node n, int indirectionIndex | n = callOutputWithIndirectionIndex(call, output, indirectionIndex) and d > 0 | // The return value diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintDataFlowRelevantIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintDataFlowRelevantIR.qll index 2a654828ee54..22550e187ace 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintDataFlowRelevantIR.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintDataFlowRelevantIR.qll @@ -1,6 +1,6 @@ private import cpp private import semmle.code.cpp.ir.IR -private import SsaInternals as Ssa +private import SsaImpl as Ssa /** * A property provider that hides all instructions and operands that are not relevant for IR dataflow. diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll index cf612ce73687..e310db319319 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll @@ -2,7 +2,7 @@ private import cpp private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate -private import SsaInternals as Ssa +private import SsaImpl as Ssa private import PrintIRUtilities /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll similarity index 91% rename from cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll rename to cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll index cfaa625f9f5a..3af2ea38a641 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll @@ -1,4 +1,4 @@ -private import codeql.ssa.Ssa as SsaImplCommon +private import codeql.ssa.Ssa as Ssa private import semmle.code.cpp.ir.IR private import DataFlowUtil private import DataFlowImplCommon as DataFlowImplCommon @@ -12,7 +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 -import SsaInternalsCommon +import SsaImplCommon private module SourceVariables { cached @@ -884,7 +884,7 @@ private predicate baseSourceVariableIsGlobal( ) } -private module SsaInput implements SsaImplCommon::InputSig { +private module SsaInput implements Ssa::InputSig { import InputSigCommon import SourceVariables @@ -958,7 +958,7 @@ class GlobalDef extends Definition { GlobalLikeVariable getVariable() { result = impl.getVariable() } } -private module SsaImpl = SsaImplCommon::Make; +private module SsaImpl = Ssa::Make; private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationInputSig { private import codeql.util.Boolean @@ -1125,9 +1125,11 @@ class PhiNode extends Definition instanceof SsaImpl::PhiNode { /** An static single assignment (SSA) definition. */ class Definition extends SsaImpl::Definition { - // TODO: Include prior definitions of uncertain writes or rename predicate - // i.e. the disjunct `SsaImpl::uncertainWriteDefinitionInput(this, result)` - private Definition getAPhiInputOrPriorDefinition() { result = this.(PhiNode).getAnInput() } + private Definition getAPhiInputOrPriorDefinition() { + result = this.(PhiNode).getAnInput() + or + SsaImpl::uncertainWriteDefinitionInput(this, result) + } /** * Gets a definition that ultimately defines this SSA definition and is @@ -1138,6 +1140,36 @@ class Definition extends SsaImpl::Definition { not result instanceof PhiNode } + /** Gets an `Operand` that represents a use of this definition. */ + Operand getAUse() { + exists(SourceVariable sv, IRBlock bb, int i, UseImpl use | + ssaDefReachesRead(sv, this, bb, i) and + use.hasIndexInBlock(bb, i, sv) and + result = use.getNode().asOperand() + ) + } + + /** + * Gets an `Operand` that represents an indirect use of this definition. + * + * The use is indirect because the operand represents a pointer that points + * to the value written by this definition. For example in: + * ```cpp + * 1. int x = 42; + * 2. int* p = &x; + * ``` + * There is an `ExplicitDefinition` corresponding to `x = 42` on line 1 and + * the definition has an indirect use on line 2 because `&x` points to the + * value that was defined by the definition. + */ + Operand getAnIndirectUse(int indirectionIndex) { + exists(SourceVariable sv, IRBlock bb, int i, UseImpl use | + ssaDefReachesRead(sv, this, bb, i) and + use.hasIndexInBlock(bb, i, sv) and + result = use.getNode().asIndirectOperand(indirectionIndex) + ) + } + /** * INTERNAL: Do not use. */ @@ -1170,4 +1202,63 @@ class Definition extends SsaImpl::Definition { Type getUnspecifiedType() { result = this.getUnderlyingType().getUnspecifiedType() } } +/** + * An SSA definition that corresponds to an explicit definition. + */ +class ExplicitDefinition extends Definition, SsaImpl::WriteDefinition { + DefImpl def; + + ExplicitDefinition() { + exists(IRBlock bb, int i, SourceVariable sv | + this.definesAt(sv, bb, i) and + def.hasIndexInBlock(sv, bb, i) + ) + } + + /** + * Gets the `Instruction` computing the value that is written to the + * associated SSA variable by this SSA definition. + * + * If `this.getIndirectionIndex() = 0` (i.e., if `this` is an instance of + * `DirectExplicitDefinition`) then the SSA variable is present in the source + * code. + * However, if `this.getIndirectionIndex() > 0` (i.e., if `this` is an + * instance of `IndirectExplicitDefinition`) then the SSA variable associated + * with this definition represents the memory pointed to by a variable in the + * source code. + */ + Instruction getAssignedInstruction() { result = def.getValue().asInstruction() } +} + +/** + * An explicit SSA definition that writes an indirect value to a pointer. + * + * For example in: + * ```cpp + * int x = 42; // (1) + * int* p = &x; // (2) + * ``` + * There are three `ExplicitDefinition`: + * 1. A `DirectExplicitDefinition` at (1) which writes `42` to the SSA variable + * corresponding to `x`. + * 2. A `DirectExplicitDefinition` at (2) which writes `&x` to the SSA variable + * corresponding to `p`. + * 3. A `IndirectExplicitDefinition` at (2) which writes `*&x` (i.e., `x`) to + * the SSA variable corresponding to `*p`. + */ +class IndirectExplicitDefinition extends ExplicitDefinition { + IndirectExplicitDefinition() { this.getIndirectionIndex() > 0 } +} + +/** + * An SSA definition that corresponds to an explicit definition. + * + * Unlike `ExplicitDefinition` this class does not include indirect + * explicit definition. See `IndirectExplicitDefinition` if you want to include + * those. + */ +class DirectExplicitDefinition extends ExplicitDefinition { + DirectExplicitDefinition() { this.getIndirectionIndex() = 0 } +} + import SsaCached diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll similarity index 100% rename from cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll rename to cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll 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 83fac3ebb49a..f190569330f5 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,7 +5,7 @@ private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.models.interfaces.SideEffect private import DataFlowUtil private import DataFlowPrivate -private import SsaInternals as Ssa +private import SsaImpl as Ssa private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import semmle.code.cpp.ir.dataflow.FlowSteps