From 1dae78760507464a1440c0cf8e041a135b6e10f5 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 31 Jul 2025 12:58:05 +0100 Subject: [PATCH 01/11] C++: Drive-by fix suggested by Schack. This now matches the predicate in C#. --- .../semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll index cfaa625f9f5a..bd34bc9531cc 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll @@ -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 From 5a91aa2105bae529d8b9aac77f236b48ab32780a Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 31 Jul 2025 13:24:02 +0100 Subject: [PATCH 02/11] C++: Expose SSA definitions from dataflow. --- .../cpp/ir/dataflow/internal/DataFlowUtil.qll | 10 ++++ .../cpp/ir/dataflow/internal/SsaInternals.qll | 59 +++++++++++++++++++ 2 files changed, 69 insertions(+) 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..cc9855ffc445 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 @@ -2576,3 +2576,13 @@ Function getARuntimeTarget(Call call) { result = DataFlowImplCommon::viableCallableLambda(dfCall, _).asSourceCallable() ) } + +class Definition = Ssa::Definition; + +class ExplicitDefinition = Ssa::ExplicitDefinition; + +class DirectExplicitDefinition = Ssa::DirectExplicitDefinition; + +class IndirectExplicitDefinition = Ssa::IndirectExplicitDefinition; + +class PhiNode = Ssa::PhiNode; diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll index bd34bc9531cc..9d3566e3020d 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll @@ -1140,6 +1140,15 @@ class Definition extends SsaImpl::Definition { not result instanceof PhiNode } + /** Gets a `Node` that represents a use of this definition. */ + Node 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() + ) + } + /** * INTERNAL: Do not use. */ @@ -1172,4 +1181,54 @@ 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 `Node` computing the value that is written by this SSA definition. + */ + Node getAssignedValue() { result.asInstruction() = 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 vairable 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 From 8691075aaec764310057badef6d2d9da044326be Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 31 Jul 2025 13:52:21 +0100 Subject: [PATCH 03/11] Update cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll index 9d3566e3020d..914cb8c1e6c0 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll @@ -1214,7 +1214,7 @@ class ExplicitDefinition extends Definition, SsaImpl::WriteDefinition { * 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 vairable corresponding to `*p`. + * the SSA variable corresponding to `*p`. */ class IndirectExplicitDefinition extends ExplicitDefinition { IndirectExplicitDefinition() { this.getIndirectionIndex() > 0 } From 7e93b99ff9ef4f5a8644966950761004eb1783c1 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 31 Jul 2025 13:57:19 +0100 Subject: [PATCH 04/11] C++: Add change note. --- cpp/ql/lib/change-notes/2025-07-31-ssa.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2025-07-31-ssa.md 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 From c8f4b287d10548742021e2ce44c4965afa73b142 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 31 Jul 2025 14:07:38 +0100 Subject: [PATCH 05/11] C++: Add a comment on the old SSA library. --- cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll index 1e0b39be1ac7..c2d4a1c649e1 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::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 From 0d91622d182d5884753db1d194c0fc0c3bb78f6e Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 1 Aug 2025 10:34:14 +0100 Subject: [PATCH 06/11] C++: Rename SsaInternals to SsaImpl and SsaInternalsCommon to SsaImplCommon. --- .../dataflow/internal/{SsaInternals.qll => SsaImpl.qll} | 8 ++++---- .../{SsaInternalsCommon.qll => SsaImplCommon.qll} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/{SsaInternals.qll => SsaImpl.qll} (99%) rename cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/{SsaInternalsCommon.qll => SsaImplCommon.qll} (100%) 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 99% 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 914cb8c1e6c0..14cf7efae247 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 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 From 7ede3aa51650e4d291ed065d40343a24d907f7a1 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 1 Aug 2025 10:35:34 +0100 Subject: [PATCH 07/11] C++: Fix imports. --- .../ir/dataflow/internal/DataFlowPrivate.qll | 2 +- .../cpp/ir/dataflow/internal/DataFlowUtil.qll | 75 ++++++++++--------- .../cpp/ir/dataflow/internal/ModelUtil.qll | 2 +- 3 files changed, 41 insertions(+), 38 deletions(-) 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 cc9855ffc445..2a50c2261e62 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) } } 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..6c78ea7debca 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 @@ -7,7 +7,7 @@ private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.dataflow.DataFlow 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`. From 32e6d0934ecb51d748b7dedc282b53b025b9e3fc Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 1 Aug 2025 10:36:28 +0100 Subject: [PATCH 08/11] C++: Drive-by fix: These files imported both the public dataflow files and the internal ones. Let's only import the internal ones. --- .../ir/dataflow/internal/DataFlowDispatch.qll | 25 ++++++++----------- .../cpp/ir/dataflow/internal/ModelUtil.qll | 10 ++++---- .../internal/PrintDataFlowRelevantIR.qll | 2 +- .../ir/dataflow/internal/PrintIRLocalFlow.qll | 2 +- .../dataflow/internal/TaintTrackingUtil.qll | 2 +- 5 files changed, 18 insertions(+), 23 deletions(-) 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/ModelUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll index 6c78ea7debca..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,7 +4,7 @@ */ 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 SsaImpl as Ssa @@ -12,7 +12,7 @@ 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/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 From 33d05984c8ae6c22ed441d014b8782d48c84b368 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 1 Aug 2025 10:37:04 +0100 Subject: [PATCH 09/11] C++: Stick the exposed SSA classes into a public SSA module. --- .../code/cpp/ir/dataflow/internal/DataFlowUtil.qll | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 2a50c2261e62..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 @@ -2580,12 +2580,15 @@ Function getARuntimeTarget(Call call) { ) } -class Definition = Ssa::Definition; +/** A module that provides static single assignment (SSA) information. */ +module Ssa { + class Definition = SsaImpl::Definition; -class ExplicitDefinition = Ssa::ExplicitDefinition; + class ExplicitDefinition = SsaImpl::ExplicitDefinition; -class DirectExplicitDefinition = Ssa::DirectExplicitDefinition; + class DirectExplicitDefinition = SsaImpl::DirectExplicitDefinition; -class IndirectExplicitDefinition = Ssa::IndirectExplicitDefinition; + class IndirectExplicitDefinition = SsaImpl::IndirectExplicitDefinition; -class PhiNode = Ssa::PhiNode; + class PhiNode = SsaImpl::PhiNode; +} From b70836e241e89ac7f2b1ea1b52ba3af76606e0ea Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 1 Aug 2025 11:30:46 +0100 Subject: [PATCH 10/11] C++: Modify the API to not expose dataflow nodes. --- .../code/cpp/ir/dataflow/internal/SsaImpl.qll | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) 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 14cf7efae247..3af2ea38a641 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 @@ -1140,12 +1140,33 @@ class Definition extends SsaImpl::Definition { not result instanceof PhiNode } - /** Gets a `Node` that represents a use of this definition. */ - Node getAUse() { + /** 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() + 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) ) } @@ -1195,9 +1216,18 @@ class ExplicitDefinition extends Definition, SsaImpl::WriteDefinition { } /** - * Gets the `Node` computing the value that is written by this SSA definition. + * 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. */ - Node getAssignedValue() { result.asInstruction() = def.getValue().asInstruction() } + Instruction getAssignedInstruction() { result = def.getValue().asInstruction() } } /** From 0e9286dd3412f0fed412df7ad8341d40058ac956 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 1 Aug 2025 11:37:12 +0100 Subject: [PATCH 11/11] C++: Fix QLDoc. --- cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll index c2d4a1c649e1..9c6069d4a0c5 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll @@ -19,7 +19,7 @@ class StandardSsa extends SsaHelper { * library: * ``` * import semmle.code.cpp.dataflow.new.DataFlow - * // use `DataFlow::Definition` + * // use `DataFlow::Ssa::Definition` * ``` * * A definition of one or more SSA variables, including phi node definitions.