From 42eef9e4b7a269ec58f8bf3ba87ce150bd807993 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 25 Feb 2025 11:59:38 +0100 Subject: [PATCH 01/12] SSA: Deprecate getDefinitionExt. --- shared/ssa/codeql/ssa/Ssa.qll | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index bdf571923737..69975a9ba536 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1644,7 +1644,7 @@ module Make Input> { /** A synthesized SSA data flow node. */ abstract private class SsaNodeImpl extends NodeImpl { /** Gets the underlying SSA definition. */ - abstract DefinitionExt getDefinitionExt(); + abstract deprecated DefinitionExt getDefinitionExt(); /** Gets the SSA definition this node corresponds to, if any. */ Definition asDefinition() { this = TSsaDefinitionNode(result) } @@ -1664,7 +1664,9 @@ module Make Input> { SsaDefinitionExtNodeImpl() { this = TSsaDefinitionNode(def) } - override DefinitionExt getDefinitionExt() { result = def } + DefinitionExt getDefExt() { result = def } + + deprecated override DefinitionExt getDefinitionExt() { result = def } override BasicBlock getBasicBlock() { result = def.getBasicBlock() } @@ -1692,7 +1694,7 @@ module Make Input> { /** A node that represents a synthetic read of a source variable. */ final class SsaSynthReadNode extends SsaNode { SsaSynthReadNode() { - this.(SsaDefinitionExtNodeImpl).getDefinitionExt() instanceof PhiReadNode or + this.(SsaDefinitionExtNodeImpl).getDefExt() instanceof PhiReadNode or this instanceof SsaInputNodeImpl } } @@ -1744,7 +1746,9 @@ module Make Input> { input = input_ } - override SsaInputDefinitionExt getDefinitionExt() { result = def_ } + SsaInputDefinitionExt getPhi() { result = def_ } + + deprecated override SsaInputDefinitionExt getDefinitionExt() { result = def_ } override BasicBlock getBasicBlock() { result = input_ } @@ -1767,7 +1771,7 @@ module Make Input> { private predicate flowOutOf( DefinitionExt def, Node nodeFrom, SourceVariable v, BasicBlock bb, int i, boolean isUseStep ) { - nodeFrom.(SsaDefinitionExtNodeImpl).getDefinitionExt() = def and + nodeFrom.(SsaDefinitionExtNodeImpl).getDefExt() = def and def.definesAt(v, bb, i, _) and isUseStep = false or @@ -1790,7 +1794,7 @@ module Make Input> { // Flow from parameter into entry definition DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter()) ) and - nodeTo.(SsaDefinitionExtNodeImpl).getDefinitionExt() = def and + nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def and isUseStep = false or // Flow from definition/read to next read @@ -1806,7 +1810,7 @@ module Make Input> { AdjacentSsaRefs::adjacentRefRead(bb1, i1, bb2, i2, v) and exists(UncertainWriteDefinition def2 | DfInput::allowFlowIntoUncertainDef(def2) and - nodeTo.(SsaDefinitionExtNodeImpl).getDefinitionExt() = def2 and + nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def2 and def2.definesAt(v, bb2, i2) ) ) @@ -1823,8 +1827,8 @@ module Make Input> { ) or // Flow from input node to def - nodeTo.(SsaDefinitionExtNodeImpl).getDefinitionExt() = def and - def = nodeFrom.(SsaInputNodeImpl).getDefinitionExt() and + nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def and + def = nodeFrom.(SsaInputNodeImpl).getPhi() and isUseStep = false } @@ -1837,10 +1841,10 @@ module Make Input> { // Flow from parameter into entry definition DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter()) ) and - nodeTo.(SsaDefinitionExtNodeImpl).getDefinitionExt() = def + nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def or // Flow from SSA definition to read - nodeFrom.(SsaDefinitionExtNodeImpl).getDefinitionExt() = def and + nodeFrom.(SsaDefinitionExtNodeImpl).getDefExt() = def and nodeTo.(ExprNode).getExpr() = DfInput::getARead(def) } From 39bba7f5c2cc5440c8cd9c30b59948ea30afc1c9 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 25 Feb 2025 13:13:56 +0100 Subject: [PATCH 02/12] SSA: Change a few DefinitionExt uses that are actually just Definitions. --- shared/ssa/codeql/ssa/Ssa.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index 69975a9ba536..e1d4dd248cdc 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1794,7 +1794,7 @@ module Make Input> { // Flow from parameter into entry definition DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter()) ) and - nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def and + nodeTo.(SsaDefinitionNode).getDefinition() = def and isUseStep = false or // Flow from definition/read to next read @@ -1810,7 +1810,7 @@ module Make Input> { AdjacentSsaRefs::adjacentRefRead(bb1, i1, bb2, i2, v) and exists(UncertainWriteDefinition def2 | DfInput::allowFlowIntoUncertainDef(def2) and - nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def2 and + nodeTo.(SsaDefinitionNode).getDefinition() = def2 and def2.definesAt(v, bb2, i2) ) ) @@ -1841,7 +1841,7 @@ module Make Input> { // Flow from parameter into entry definition DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter()) ) and - nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def + nodeTo.(SsaDefinitionNode).getDefinition() = def or // Flow from SSA definition to read nodeFrom.(SsaDefinitionExtNodeImpl).getDefExt() = def and From 9e03b12ba0da51b57268f470a095fe4897112aea Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 25 Feb 2025 14:54:23 +0100 Subject: [PATCH 03/12] C#/Java/Ruby/Rust/SSA: Replace DefinitionExt with SourceVariable in data flow integration predicates. --- .../dataflow/internal/DataFlowPrivate.qll | 38 +++++------ .../code/csharp/dataflow/internal/SsaImpl.qll | 8 +-- .../java/dataflow/internal/DataFlowNodes.qll | 10 ++- .../java/dataflow/internal/DataFlowUtil.qll | 2 +- .../code/java/dataflow/internal/SsaImpl.qll | 10 ++- .../dataflow/internal/DataFlowPrivate.qll | 18 ++--- .../codeql/ruby/dataflow/internal/SsaImpl.qll | 8 +-- .../rust/dataflow/internal/DataFlowImpl.qll | 22 +++--- .../codeql/rust/dataflow/internal/SsaImpl.qll | 10 +-- shared/ssa/codeql/ssa/Ssa.qll | 67 ++++++++++--------- 10 files changed, 98 insertions(+), 95 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 11f59d59752f..f7b6fd5ff137 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -456,9 +456,9 @@ module VariableCapture { Flow::clearsContent(asClosureNode(node), getCapturedVariableContent(c)) } - class CapturedSsaDefinitionExt extends SsaImpl::DefinitionExt { - CapturedSsaDefinitionExt() { - this.getSourceVariable().getAssignable() = any(CapturedVariable v).asLocalScopeVariable() + class CapturedSsaSourceVariable extends Ssa::SourceVariable { + CapturedSsaSourceVariable() { + this.getAssignable() = any(CapturedVariable v).asLocalScopeVariable() } } @@ -509,12 +509,12 @@ module SsaFlow { result.(Impl::ParameterNode).getParameter() = n.(ExplicitParameterNode).getSsaDefinition() } - predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) { - Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep) + predicate localFlowStep(Ssa::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { + Impl::localFlowStep(v, asNode(nodeFrom), asNode(nodeTo), isUseStep) } - predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) { - Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo)) + predicate localMustFlowStep(Ssa::SourceVariable v, Node nodeFrom, Node nodeTo) { + Impl::localMustFlowStep(v, asNode(nodeFrom), asNode(nodeTo)) } } @@ -644,12 +644,10 @@ module LocalFlow { } /** - * Holds if the source variable of SSA definition `def` is an instance field. + * Holds if the source variable `v` is an instance field. */ - predicate usesInstanceField(SsaImpl::DefinitionExt def) { - exists(Ssa::SourceVariables::FieldOrPropSourceVariable fp | fp = def.getSourceVariable() | - not fp.getAssignable().(Modifiable).isStatic() - ) + predicate isInstanceField(Ssa::SourceVariables::FieldOrPropSourceVariable v) { + not v.getAssignable().(Modifiable).isStatic() } predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { @@ -749,10 +747,10 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) { ( LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or - exists(SsaImpl::DefinitionExt def, boolean isUseStep | - SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and - not LocalFlow::usesInstanceField(def) and - not def instanceof VariableCapture::CapturedSsaDefinitionExt + exists(Ssa::SourceVariable v, boolean isUseStep | + SsaFlow::localFlowStep(v, nodeFrom, nodeTo, isUseStep) and + not LocalFlow::isInstanceField(v) and + not v instanceof VariableCapture::CapturedSsaSourceVariable | isUseStep = false or @@ -3007,13 +3005,13 @@ private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) { /** Extra data-flow steps needed for lambda flow analysis. */ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { - exists(SsaImpl::DefinitionExt def | - SsaFlow::localFlowStep(def, nodeFrom, nodeTo, _) and + exists(Ssa::SourceVariable v | + SsaFlow::localFlowStep(v, nodeFrom, nodeTo, _) and preservesValue = true | - LocalFlow::usesInstanceField(def) + LocalFlow::isInstanceField(v) or - def instanceof VariableCapture::CapturedSsaDefinitionExt + v instanceof VariableCapture::CapturedSsaSourceVariable ) or delegateCreationStep(nodeFrom, nodeTo) and diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll index 61787debca4f..34dfe62a36d3 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -967,13 +967,13 @@ private module Cached { import DataFlowIntegrationImpl cached - predicate localFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) { - DataFlowIntegrationImpl::localFlowStep(def, nodeFrom, nodeTo, isUseStep) + predicate localFlowStep(Ssa::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { + DataFlowIntegrationImpl::localFlowStep(v, nodeFrom, nodeTo, isUseStep) } cached - predicate localMustFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo) { - DataFlowIntegrationImpl::localMustFlowStep(def, nodeFrom, nodeTo) + predicate localMustFlowStep(Ssa::SourceVariable v, Node nodeFrom, Node nodeTo) { + DataFlowIntegrationImpl::localMustFlowStep(v, nodeFrom, nodeTo) } signature predicate guardChecksSig(Guards::Guard g, Expr e, Guards::AbstractValue v); diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll index fd14bcfd100c..7e1b10f58e36 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll @@ -36,14 +36,12 @@ module SsaFlow { TExplicitParameterNode(result.(Impl::ParameterNode).getParameter()) = n } - predicate localFlowStep( - SsaImpl::Impl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep - ) { - Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep) + predicate localFlowStep(SsaSourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { + Impl::localFlowStep(v, asNode(nodeFrom), asNode(nodeTo), isUseStep) } - predicate localMustFlowStep(SsaImpl::Impl::DefinitionExt def, Node nodeFrom, Node nodeTo) { - Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo)) + predicate localMustFlowStep(Node nodeFrom, Node nodeTo) { + Impl::localMustFlowStep(_, asNode(nodeFrom), asNode(nodeTo)) } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll index cc03f227151d..e87c92f3d6c5 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -168,7 +168,7 @@ predicate localMustFlowStep(Node node1, Node node2) { node2.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getEnclosingCallable() ) or - SsaFlow::localMustFlowStep(_, node1, node2) + SsaFlow::localMustFlowStep(node1, node2) or node2.asExpr().(CastingExpr).getExpr() = node1.asExpr() or diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll index d5fdf4ef829a..d5343834ffe2 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll @@ -544,15 +544,13 @@ private module Cached { import DataFlowIntegrationImpl cached - predicate localFlowStep(Impl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) { - not def instanceof UntrackedDef and - DataFlowIntegrationImpl::localFlowStep(def, nodeFrom, nodeTo, isUseStep) + predicate localFlowStep(TrackedVar v, Node nodeFrom, Node nodeTo, boolean isUseStep) { + DataFlowIntegrationImpl::localFlowStep(v, nodeFrom, nodeTo, isUseStep) } cached - predicate localMustFlowStep(Impl::DefinitionExt def, Node nodeFrom, Node nodeTo) { - not def instanceof UntrackedDef and - DataFlowIntegrationImpl::localMustFlowStep(def, nodeFrom, nodeTo) + predicate localMustFlowStep(TrackedVar v, Node nodeFrom, Node nodeTo) { + DataFlowIntegrationImpl::localMustFlowStep(v, nodeFrom, nodeTo) } signature predicate guardChecksSig(Guards::Guard g, Expr e, boolean branch); diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index 004944c83e4a..d477550d1e84 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -111,12 +111,14 @@ module SsaFlow { n = toParameterNode(result.(Impl::ParameterNode).getParameter()) } - predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) { - Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep) + predicate localFlowStep( + SsaImpl::SsaInput::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep + ) { + Impl::localFlowStep(v, asNode(nodeFrom), asNode(nodeTo), isUseStep) } - predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) { - Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo)) + predicate localMustFlowStep(Node nodeFrom, Node nodeTo) { + Impl::localMustFlowStep(_, asNode(nodeFrom), asNode(nodeTo)) } } @@ -175,7 +177,7 @@ module LocalFlow { } predicate localMustFlowStep(Node node1, Node node2) { - SsaFlow::localMustFlowStep(_, node1, node2) + SsaFlow::localMustFlowStep(node1, node2) or node1.asExpr() = node2.asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs() or @@ -525,10 +527,10 @@ private module Cached { ( LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or - exists(SsaImpl::DefinitionExt def, boolean isUseStep | - SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and + exists(SsaImpl::SsaInput::SourceVariable v, boolean isUseStep | + SsaFlow::localFlowStep(v, nodeFrom, nodeTo, isUseStep) and // captured variables are handled by the shared `VariableCapture` library - not def instanceof VariableCapture::CapturedSsaDefinitionExt + not v instanceof VariableCapture::CapturedVariable | isUseStep = false or diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index b8757e489dcf..6b37c29e978c 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -387,13 +387,13 @@ private module Cached { import DataFlowIntegrationImpl cached - predicate localFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) { - DataFlowIntegrationImpl::localFlowStep(def, nodeFrom, nodeTo, isUseStep) + predicate localFlowStep(SsaInput::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { + DataFlowIntegrationImpl::localFlowStep(v, nodeFrom, nodeTo, isUseStep) } cached - predicate localMustFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo) { - DataFlowIntegrationImpl::localMustFlowStep(def, nodeFrom, nodeTo) + predicate localMustFlowStep(SsaInput::SourceVariable v, Node nodeFrom, Node nodeTo) { + DataFlowIntegrationImpl::localMustFlowStep(v, nodeFrom, nodeTo) } signature predicate guardChecksSig(Cfg::CfgNodes::AstCfgNode g, Cfg::CfgNode e, boolean branch); diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 4024f5974cf8..6c2d2a4db011 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -608,12 +608,14 @@ module SsaFlow { n = toParameterNode(result.(SsaFlow::ParameterNode).getParameter()) } - predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) { - SsaFlow::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep) + predicate localFlowStep( + SsaImpl::SsaInput::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep + ) { + SsaFlow::localFlowStep(v, asNode(nodeFrom), asNode(nodeTo), isUseStep) } - predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) { - SsaFlow::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo)) + predicate localMustFlowStep(Node nodeFrom, Node nodeTo) { + SsaFlow::localMustFlowStep(_, asNode(nodeFrom), asNode(nodeTo)) } } @@ -1205,9 +1207,9 @@ module RustDataFlow implements InputSig { ( LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or - exists(SsaImpl::DefinitionExt def, boolean isUseStep | - SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and - not def instanceof VariableCapture::CapturedSsaDefinitionExt + exists(SsaImpl::SsaInput::SourceVariable v, boolean isUseStep | + SsaFlow::localFlowStep(v, nodeFrom, nodeTo, isUseStep) and + not v instanceof VariableCapture::CapturedVariable | isUseStep = false or @@ -1514,7 +1516,7 @@ module RustDataFlow implements InputSig { * must also apply to `node1`. */ predicate localMustFlowStep(Node node1, Node node2) { - SsaFlow::localMustFlowStep(_, node1, node2) + SsaFlow::localMustFlowStep(node1, node2) or FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1 .(Node::FlowSummaryNode) @@ -1688,10 +1690,6 @@ module VariableCapture { predicate clearsContent(Node node, CapturedVariableContent c) { Flow::clearsContent(asClosureNode(node), c.getVariable()) } - - class CapturedSsaDefinitionExt extends SsaImpl::DefinitionExt { - CapturedSsaDefinitionExt() { this.getSourceVariable() instanceof CapturedVariable } - } } import MakeImpl diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll index 87a8d6c0b0bd..f8a53450cf3f 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll @@ -303,13 +303,15 @@ private module Cached { import DataFlowIntegrationImpl cached - predicate localFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) { - DataFlowIntegrationImpl::localFlowStep(def, nodeFrom, nodeTo, isUseStep) + predicate localFlowStep( + SsaInput::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep + ) { + DataFlowIntegrationImpl::localFlowStep(v, nodeFrom, nodeTo, isUseStep) } cached - predicate localMustFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo) { - DataFlowIntegrationImpl::localMustFlowStep(def, nodeFrom, nodeTo) + predicate localMustFlowStep(SsaInput::SourceVariable v, Node nodeFrom, Node nodeTo) { + DataFlowIntegrationImpl::localMustFlowStep(v, nodeFrom, nodeTo) } signature predicate guardChecksSig(CfgNodes::AstCfgNode g, Cfg::CfgNode e, boolean branch); diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index e1d4dd248cdc..ada62af51819 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1765,17 +1765,17 @@ module Make Input> { * Holds if `nodeFrom` corresponds to the reference to `v` at index `i` in * `bb`. The boolean `isUseStep` indicates whether `nodeFrom` is an actual * read. If it is false then `nodeFrom` may be any of the following: an - * uncertain write, a certain write, a phi, or a phi read. `def` is the SSA - * definition that is read/defined at `nodeFrom`. + * uncertain write, a certain write, a phi, or a phi read. */ private predicate flowOutOf( - DefinitionExt def, Node nodeFrom, SourceVariable v, BasicBlock bb, int i, boolean isUseStep + Node nodeFrom, SourceVariable v, BasicBlock bb, int i, boolean isUseStep ) { - nodeFrom.(SsaDefinitionExtNodeImpl).getDefExt() = def and - def.definesAt(v, bb, i, _) and - isUseStep = false + exists(DefinitionExt def | + nodeFrom.(SsaDefinitionExtNodeImpl).getDefExt() = def and + def.definesAt(v, bb, i, _) and + isUseStep = false + ) or - ssaDefReachesReadExt(v, def, bb, i) and [nodeFrom, nodeFrom.(ExprPostUpdateNode).getPreUpdateNode()].(ReadNode).readsAt(bb, i, v) and isUseStep = true } @@ -1786,27 +1786,29 @@ module Make Input> { * `isUseStep` is `true` when `nodeFrom` is a (post-update) read node and * `nodeTo` is a read node or phi (read) node. */ - predicate localFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) { - ( + predicate localFlowStep(SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { + exists(Definition def | // Flow from assignment into SSA definition DfInput::ssaDefAssigns(def, nodeFrom.(ExprNode).getExpr()) or // Flow from parameter into entry definition DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter()) - ) and - nodeTo.(SsaDefinitionNode).getDefinition() = def and - isUseStep = false + | + nodeTo.(SsaDefinitionNode).getDefinition() = def and + v = def.getSourceVariable() and + isUseStep = false + ) or // Flow from definition/read to next read - exists(SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 | - flowOutOf(def, nodeFrom, v, bb1, i1, isUseStep) and + exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 | + flowOutOf(nodeFrom, v, bb1, i1, isUseStep) and AdjacentSsaRefs::adjacentRefRead(bb1, i1, bb2, i2, v) and nodeTo.(ReadNode).readsAt(bb2, i2, v) ) or // Flow from definition/read to next uncertain write - exists(SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 | - flowOutOf(def, nodeFrom, v, bb1, i1, isUseStep) and + exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 | + flowOutOf(nodeFrom, v, bb1, i1, isUseStep) and AdjacentSsaRefs::adjacentRefRead(bb1, i1, bb2, i2, v) and exists(UncertainWriteDefinition def2 | DfInput::allowFlowIntoUncertainDef(def2) and @@ -1816,36 +1818,41 @@ module Make Input> { ) or // Flow from definition/read to phi input - exists( - SourceVariable v, BasicBlock bb, int i, BasicBlock input, BasicBlock bbPhi, - DefinitionExt phi - | - flowOutOf(def, nodeFrom, v, bb, i, isUseStep) and + exists(BasicBlock bb, int i, BasicBlock input, BasicBlock bbPhi, DefinitionExt phi | + flowOutOf(nodeFrom, v, bb, i, isUseStep) and AdjacentSsaRefs::adjacentRefPhi(bb, i, input, bbPhi, v) and nodeTo = TSsaInputNode(phi, input) and phi.definesAt(v, bbPhi, -1, _) ) or // Flow from input node to def - nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def and - def = nodeFrom.(SsaInputNodeImpl).getPhi() and - isUseStep = false + exists(DefinitionExt def | + nodeTo.(SsaDefinitionExtNodeImpl).getDefExt() = def and + def = nodeFrom.(SsaInputNodeImpl).getPhi() and + v = def.getSourceVariable() and + isUseStep = false + ) } /** Holds if the value of `nodeTo` is given by `nodeFrom`. */ - predicate localMustFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo) { - ( + predicate localMustFlowStep(SourceVariable v, Node nodeFrom, Node nodeTo) { + exists(Definition def | // Flow from assignment into SSA definition DfInput::ssaDefAssigns(def, nodeFrom.(ExprNode).getExpr()) or // Flow from parameter into entry definition DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter()) - ) and - nodeTo.(SsaDefinitionNode).getDefinition() = def + | + nodeTo.(SsaDefinitionNode).getDefinition() = def and + v = def.getSourceVariable() + ) or // Flow from SSA definition to read - nodeFrom.(SsaDefinitionExtNodeImpl).getDefExt() = def and - nodeTo.(ExprNode).getExpr() = DfInput::getARead(def) + exists(DefinitionExt def | + nodeFrom.(SsaDefinitionExtNodeImpl).getDefExt() = def and + nodeTo.(ExprNode).getExpr() = DfInput::getARead(def) and + v = def.getSourceVariable() + ) } /** From 2f744ce3ecbd6845bdbc11a6313d5d4cfd0a3ef7 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 26 Feb 2025 12:16:05 +0100 Subject: [PATCH 04/12] SSA: Expose module for qltesting adjacent references. --- shared/ssa/codeql/ssa/Ssa.qll | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index ada62af51819..b95420375380 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1954,4 +1954,85 @@ module Make Input> { } } } + + /** + * Provides query predicates for testing adjacent SSA references and + * insertion of phi reads. + */ + module TestAdjacentRefs { + private newtype TRef = + TRefRead(BasicBlock bb, int i, SourceVariable v) { variableRead(bb, i, v, true) } or + TRefDef(Definition def) or + TRefPhiRead(BasicBlock bb, SourceVariable v) { synthPhiRead(bb, v) } + + /** + * An SSA reference. This is either a certain read, a definition, or a + * synthesized phi read. + */ + class Ref extends TRef { + /** Gets the source variable referenced by this reference. */ + SourceVariable getSourceVariable() { + this = TRefRead(_, _, result) + or + exists(Definition def | this = TRefDef(def) and def.getSourceVariable() = result) + or + this = TRefPhiRead(_, result) + } + + predicate isPhiRead() { this = TRefPhiRead(_, _) } + + /** Gets a textual representation of this SSA reference. */ + string toString() { + this = TRefRead(_, _, _) and result = "SSA read(" + this.getSourceVariable() + ")" + or + exists(Definition def | this = TRefDef(def) and result = def.toString()) + or + this = TRefPhiRead(_, _) and result = "SSA phi read(" + this.getSourceVariable() + ")" + } + + /** Gets the location of this SSA reference. */ + Location getLocation() { + exists(BasicBlock bb, int i | + this = TRefRead(bb, i, _) and bb.getNode(i).getLocation() = result + ) + or + exists(Definition def | this = TRefDef(def) and def.getLocation() = result) + or + exists(BasicBlock bb | this = TRefPhiRead(bb, _) and bb.getLocation() = result) + } + + /** Holds if this reference of `v` occurs in `bb` at index `i`. */ + predicate accessAt(BasicBlock bb, int i, SourceVariable v) { + this = TRefRead(bb, i, v) + or + exists(Definition def | this = TRefDef(def) and def.definesAt(v, bb, i)) + or + this = TRefPhiRead(bb, v) and i = -1 + } + } + + /** + * Holds if `r2` is a certain read or uncertain write, and `r1` is the + * unique prior reference. + */ + query predicate adjacentRefRead(Ref r1, Ref r2) { + exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v | + r1.accessAt(bb1, i1, v) and + r2.accessAt(bb2, i2, v) and + AdjacentSsaRefs::adjacentRefRead(bb1, i1, bb2, i2, v) + ) + } + + /** + * Holds if `phi` is a phi definition or phi read and `input` is one its + * inputs without any other reference in-between. + */ + query predicate adjacentRefPhi(Ref input, Ref phi) { + exists(BasicBlock bb, int i, BasicBlock bbPhi, SourceVariable v | + input.accessAt(bb, i, v) and + phi.accessAt(bbPhi, -1, v) and + AdjacentSsaRefs::adjacentRefPhi(bb, i, _, bbPhi, v) + ) + } + } } From f0993fc97e6c888493b50cefab5ede8baf197696 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 26 Feb 2025 12:16:43 +0100 Subject: [PATCH 05/12] C#: Switch test to use dedicated test module. --- .../code/csharp/dataflow/internal/SsaImpl.qll | 6 ------ .../dataflow/ssa/SSAPhiRead.expected | 21 ++++++++++++------- .../library-tests/dataflow/ssa/SSAPhiRead.ql | 20 +++++++++++------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll index 34dfe62a36d3..72e7d0eb4231 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -60,12 +60,6 @@ class PhiNode = Impl::PhiNode; module Consistency = Impl::Consistency; -module ExposedForTestingOnly { - predicate ssaDefReachesReadExt = Impl::ssaDefReachesReadExt/4; - - predicate phiHasInputFromBlockExt = Impl::phiHasInputFromBlockExt/3; -} - /** * Holds if the `i`th node of basic block `bb` reads source variable `v`. */ diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.expected b/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.expected index 970e6fce5244..6fdda8812ab2 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.expected @@ -6,7 +6,7 @@ phiReadNode | Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:8:13:8:13 | x | | Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x | | Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x | -phiReadNodeRead +phiReadNodeFirstRead | DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:63:9:63:14 | this.Field2 | DefUse.cs:80:37:80:42 | access to field Field2 | | Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:65:24:65:32 | this.LoopField | Fields.cs:65:24:65:32 | access to field LoopField | | Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:7:16:7:16 | o | Patterns.cs:20:17:20:17 | access to local variable o | @@ -15,16 +15,21 @@ phiReadNodeRead | Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:92:17:92:17 | access to local variable x | | Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:96:17:96:17 | access to local variable x | | Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:99:13:99:13 | access to local variable x | -| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:104:17:104:17 | access to local variable x | phiReadInput -| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:63:9:63:18 | SSA def(this.Field2) | -| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | +| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:64:13:64:18 | SSA read(this.Field2) | +| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:80:37:80:42 | SSA read(this.Field2) | | Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:61:17:61:17 | SSA entry def(this.LoopField) | -| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | -| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:7:16:7:23 | SSA def(o) | +| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:65:24:65:32 | SSA read(this.LoopField) | +| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:8:13:8:13 | SSA read(o) | +| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:12:18:12:18 | SSA read(o) | +| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:16:18:16:18 | SSA read(o) | | Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:61:17:61:17 | SSA entry def(this.LoopProp) | -| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | +| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:65:24:65:31 | SSA read(this.LoopProp) | | Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:24:9:24:15 | SSA phi(x) | -| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:25:16:25:16 | SSA phi read(x) | +| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:25:16:25:16 | SSA read(x) | | Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:17 | SSA def(x) | +| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:82:17:82:17 | SSA read(x) | +| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:86:17:86:17 | SSA read(x) | | Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:90:9:97:9 | SSA phi read(x) | +| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:92:17:92:17 | SSA read(x) | +| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:96:17:96:17 | SSA read(x) | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.ql b/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.ql index 8fee62217bf4..baa59bc5b677 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.ql +++ b/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.ql @@ -1,17 +1,21 @@ import csharp import semmle.code.csharp.dataflow.internal.SsaImpl -import ExposedForTestingOnly +import Impl::TestAdjacentRefs as RefTest -query predicate phiReadNode(PhiReadNode phi, Ssa::SourceVariable v) { phi.getSourceVariable() = v } +query predicate phiReadNode(RefTest::Ref phi, Ssa::SourceVariable v) { + phi.isPhiRead() and phi.getSourceVariable() = v +} -query predicate phiReadNodeRead(PhiReadNode phi, Ssa::SourceVariable v, ControlFlow::Node read) { - phi.getSourceVariable() = v and - exists(ControlFlow::BasicBlock bb, int i | - ssaDefReachesReadExt(v, phi, bb, i) and +query predicate phiReadNodeFirstRead(RefTest::Ref phi, Ssa::SourceVariable v, ControlFlow::Node read) { + exists(RefTest::Ref r, ControlFlow::BasicBlock bb, int i | + phi.isPhiRead() and + RefTest::adjacentRefRead(phi, r) and + r.accessAt(bb, i, v) and read = bb.getNode(i) ) } -query predicate phiReadInput(PhiReadNode phi, DefinitionExt inp) { - phiHasInputFromBlockExt(phi, inp, _) +query predicate phiReadInput(RefTest::Ref phi, RefTest::Ref inp) { + phi.isPhiRead() and + RefTest::adjacentRefPhi(inp, phi) } From 122034fe8c74f17594742acbb74d6731ac6aad29 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 26 Feb 2025 12:24:08 +0100 Subject: [PATCH 06/12] Ruby: Switch test to use dedicated test module. --- .../codeql/ruby/dataflow/internal/SsaImpl.qll | 10 +++------- .../test/library-tests/variables/ssa.expected | 14 ++++++++----- ruby/ql/test/library-tests/variables/ssa.ql | 20 +++++++++++-------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index 6b37c29e978c..f08178465b91 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -75,12 +75,6 @@ class PhiNode = Impl::PhiNode; module Consistency = Impl::Consistency; -module ExposedForTestingOnly { - predicate ssaDefReachesReadExt = Impl::ssaDefReachesReadExt/4; - - predicate phiHasInputFromBlockExt = Impl::phiHasInputFromBlockExt/3; -} - /** Holds if `v` is uninitialized at index `i` in entry block `bb`. */ predicate uninitializedWrite(Cfg::EntryBasicBlock bb, int i, LocalVariable v) { v.getDeclaringScope() = bb.getScope() and @@ -387,7 +381,9 @@ private module Cached { import DataFlowIntegrationImpl cached - predicate localFlowStep(SsaInput::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { + predicate localFlowStep( + SsaInput::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep + ) { DataFlowIntegrationImpl::localFlowStep(v, nodeFrom, nodeTo, isUseStep) } diff --git a/ruby/ql/test/library-tests/variables/ssa.expected b/ruby/ql/test/library-tests/variables/ssa.expected index b998016a39ff..8e69feef15b1 100644 --- a/ruby/ql/test/library-tests/variables/ssa.expected +++ b/ruby/ql/test/library-tests/variables/ssa.expected @@ -597,7 +597,7 @@ phiReadNode | ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | | ssa.rb:94:3:95:10 | SSA phi read(self) | ssa.rb:90:1:103:3 | self | | ssa.rb:94:3:95:10 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | -phiReadNodeRead +phiReadNodeFirstRead | parameters.rb:26:3:26:11 | SSA phi read(name) | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name | | ssa.rb:5:3:13:5 | SSA phi read(self) | ssa.rb:1:1:16:3 | self | ssa.rb:15:3:15:8 | self | | ssa.rb:19:9:19:9 | SSA phi read(self) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self | @@ -607,12 +607,16 @@ phiReadNodeRead | ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | ssa.rb:101:10:101:10 | x | phiReadInput | parameters.rb:26:3:26:11 | SSA phi read(name) | parameters.rb:25:15:25:18 | name | -| ssa.rb:5:3:13:5 | SSA phi read(self) | ssa.rb:1:1:16:3 | self (m) | +| parameters.rb:26:3:26:11 | SSA phi read(name) | parameters.rb:25:40:25:43 | SSA read(name) | +| ssa.rb:5:3:13:5 | SSA phi read(self) | ssa.rb:8:5:8:14 | SSA read(self) | +| ssa.rb:5:3:13:5 | SSA phi read(self) | ssa.rb:12:5:12:14 | SSA read(self) | | ssa.rb:19:9:19:9 | SSA phi read(self) | ssa.rb:18:1:23:3 | self (m1) | -| ssa.rb:19:9:19:9 | SSA phi read(self) | ssa.rb:19:9:19:9 | SSA phi read(self) | -| ssa.rb:92:3:96:5 | SSA phi read(self) | ssa.rb:90:1:103:3 | self (m12) | +| ssa.rb:19:9:19:9 | SSA phi read(self) | ssa.rb:20:5:20:10 | SSA read(self) | +| ssa.rb:92:3:96:5 | SSA phi read(self) | ssa.rb:93:5:93:10 | SSA read(self) | | ssa.rb:92:3:96:5 | SSA phi read(self) | ssa.rb:94:3:95:10 | SSA phi read(self) | -| ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | +| ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:93:10:93:10 | SSA read(x) | | ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:94:3:95:10 | SSA phi read(x) | | ssa.rb:94:3:95:10 | SSA phi read(self) | ssa.rb:90:1:103:3 | self (m12) | +| ssa.rb:94:3:95:10 | SSA phi read(self) | ssa.rb:95:5:95:10 | SSA read(self) | | ssa.rb:94:3:95:10 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | +| ssa.rb:94:3:95:10 | SSA phi read(x) | ssa.rb:95:10:95:10 | SSA read(x) | diff --git a/ruby/ql/test/library-tests/variables/ssa.ql b/ruby/ql/test/library-tests/variables/ssa.ql index a010eb9164d7..fb0c8e556d19 100644 --- a/ruby/ql/test/library-tests/variables/ssa.ql +++ b/ruby/ql/test/library-tests/variables/ssa.ql @@ -2,7 +2,7 @@ import codeql.ruby.AST import codeql.ruby.CFG import codeql.ruby.dataflow.SSA import codeql.ruby.dataflow.internal.SsaImpl -import ExposedForTestingOnly +import Impl::TestAdjacentRefs as RefTest query predicate definition(Ssa::Definition def, Variable v) { def.getSourceVariable() = v } @@ -23,16 +23,20 @@ query predicate phi(Ssa::PhiNode phi, Variable v, Ssa::Definition input) { phi.getSourceVariable() = v and input = phi.getAnInput() } -query predicate phiReadNode(PhiReadNode phi, Variable v) { phi.getSourceVariable() = v } +query predicate phiReadNode(RefTest::Ref phi, Variable v) { + phi.isPhiRead() and phi.getSourceVariable() = v +} -query predicate phiReadNodeRead(PhiReadNode phi, Variable v, CfgNode read) { - phi.getSourceVariable() = v and - exists(BasicBlock bb, int i | - ssaDefReachesReadExt(v, phi, bb, i) and +query predicate phiReadNodeFirstRead(RefTest::Ref phi, Variable v, CfgNode read) { + exists(RefTest::Ref r, BasicBlock bb, int i | + phi.isPhiRead() and + RefTest::adjacentRefRead(phi, r) and + r.accessAt(bb, i, v) and read = bb.getNode(i) ) } -query predicate phiReadInput(PhiReadNode phi, DefinitionExt inp) { - phiHasInputFromBlockExt(phi, inp, _) +query predicate phiReadInput(RefTest::Ref phi, RefTest::Ref inp) { + phi.isPhiRead() and + RefTest::adjacentRefPhi(inp, phi) } From 8474a47c2bbb9a93c2f737baaf624ce7c8a5bc70 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 26 Feb 2025 12:55:54 +0100 Subject: [PATCH 07/12] Rust: Switch test to use dedicated test module. --- .../codeql/rust/dataflow/internal/SsaImpl.qll | 6 ------ .../test/library-tests/variables/Ssa.expected | 7 ++++--- rust/ql/test/library-tests/variables/Ssa.ql | 20 +++++++++++-------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll index f8a53450cf3f..060393129eae 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll @@ -100,12 +100,6 @@ class PhiDefinition = Impl::PhiNode; module Consistency = Impl::Consistency; -module ExposedForTestingOnly { - predicate ssaDefReachesReadExt = Impl::ssaDefReachesReadExt/4; - - predicate phiHasInputFromBlockExt = Impl::phiHasInputFromBlockExt/3; -} - /** Holds if `v` is read at index `i` in basic block `bb`. */ private predicate variableReadActual(BasicBlock bb, int i, Variable v) { exists(VariableAccess read | diff --git a/rust/ql/test/library-tests/variables/Ssa.expected b/rust/ql/test/library-tests/variables/Ssa.expected index 619283dc4b5a..05c6f57045d3 100644 --- a/rust/ql/test/library-tests/variables/Ssa.expected +++ b/rust/ql/test/library-tests/variables/Ssa.expected @@ -535,14 +535,15 @@ phi phiReadNode | main.rs:104:11:105:12 | SSA phi read(s1) | main.rs:102:9:102:10 | s1 | | main.rs:493:5:497:5 | SSA phi read(x) | main.rs:492:9:492:9 | x | -phiReadNodeRead +phiReadNodeFirstRead | main.rs:104:11:105:12 | SSA phi read(s1) | main.rs:102:9:102:10 | s1 | main.rs:105:11:105:12 | s1 | | main.rs:493:5:497:5 | SSA phi read(x) | main.rs:492:9:492:9 | x | main.rs:500:19:500:19 | x | | main.rs:493:5:497:5 | SSA phi read(x) | main.rs:492:9:492:9 | x | main.rs:502:19:502:19 | x | phiReadInput | main.rs:104:11:105:12 | SSA phi read(s1) | main.rs:102:9:102:10 | s1 | -| main.rs:104:11:105:12 | SSA phi read(s1) | main.rs:104:11:105:12 | SSA phi read(s1) | -| main.rs:493:5:497:5 | SSA phi read(x) | main.rs:492:9:492:9 | x | +| main.rs:104:11:105:12 | SSA phi read(s1) | main.rs:105:11:105:12 | SSA read(s1) | +| main.rs:493:5:497:5 | SSA phi read(x) | main.rs:494:19:494:19 | SSA read(x) | +| main.rs:493:5:497:5 | SSA phi read(x) | main.rs:496:19:496:19 | SSA read(x) | ultimateDef | main.rs:191:9:191:44 | phi | main.rs:191:22:191:23 | a3 | | main.rs:191:9:191:44 | phi | main.rs:191:42:191:43 | a3 | diff --git a/rust/ql/test/library-tests/variables/Ssa.ql b/rust/ql/test/library-tests/variables/Ssa.ql index 3c72849f77ac..c972bb2747cd 100644 --- a/rust/ql/test/library-tests/variables/Ssa.ql +++ b/rust/ql/test/library-tests/variables/Ssa.ql @@ -3,7 +3,7 @@ import codeql.rust.controlflow.BasicBlocks import codeql.rust.controlflow.ControlFlowGraph import codeql.rust.dataflow.Ssa import codeql.rust.dataflow.internal.SsaImpl -import ExposedForTestingOnly +import Impl::TestAdjacentRefs as RefTest query predicate definition(Ssa::Definition def, Variable v) { def.getSourceVariable() = v } @@ -24,18 +24,22 @@ query predicate phi(Ssa::PhiDefinition phi, Variable v, Ssa::Definition input) { phi.getSourceVariable() = v and input = phi.getAnInput() } -query predicate phiReadNode(PhiReadNode phi, Variable v) { phi.getSourceVariable() = v } +query predicate phiReadNode(RefTest::Ref phi, Variable v) { + phi.isPhiRead() and phi.getSourceVariable() = v +} -query predicate phiReadNodeRead(PhiReadNode phi, Variable v, CfgNode read) { - phi.getSourceVariable() = v and - exists(BasicBlock bb, int i | - ssaDefReachesReadExt(v, phi, bb, i) and +query predicate phiReadNodeFirstRead(RefTest::Ref phi, Variable v, CfgNode read) { + exists(RefTest::Ref r, BasicBlock bb, int i | + phi.isPhiRead() and + RefTest::adjacentRefRead(phi, r) and + r.accessAt(bb, i, v) and read = bb.getNode(i) ) } -query predicate phiReadInput(PhiReadNode phi, DefinitionExt inp) { - phiHasInputFromBlockExt(phi, inp, _) +query predicate phiReadInput(RefTest::Ref phi, RefTest::Ref inp) { + phi.isPhiRead() and + RefTest::adjacentRefPhi(inp, phi) } query predicate ultimateDef(Ssa::Definition def, Definition ult) { From 00b8c80c24b0b086e6648a17b60e60921718b7b8 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 26 Feb 2025 14:01:01 +0100 Subject: [PATCH 08/12] SSA/C#/Ruby/Rust: Clean up SSA consistency queries. The RelevantDefinition class is no longer needed since the introduction of LocationSig. --- .../ql/consistency-queries/SsaConsistency.ql | 16 ----- ruby/ql/consistency-queries/SsaConsistency.ql | 16 ----- rust/ql/consistency-queries/SsaConsistency.ql | 16 ----- shared/ssa/codeql/ssa/Ssa.qll | 58 +------------------ 4 files changed, 3 insertions(+), 103 deletions(-) diff --git a/csharp/ql/consistency-queries/SsaConsistency.ql b/csharp/ql/consistency-queries/SsaConsistency.ql index 225aeb4e6de4..e9c9191b63a1 100644 --- a/csharp/ql/consistency-queries/SsaConsistency.ql +++ b/csharp/ql/consistency-queries/SsaConsistency.ql @@ -3,22 +3,6 @@ import semmle.code.csharp.dataflow.internal.SsaImpl as Impl import Impl::Consistency import Ssa -class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -class MyRelevantDefinitionExt extends RelevantDefinitionExt, Impl::DefinitionExt { - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - query predicate localDeclWithSsaDef(LocalVariableDeclExpr d) { // Local variables in C# must be initialized before every use, so uninitialized // local variables should not have an SSA definition, as that would imply that diff --git a/ruby/ql/consistency-queries/SsaConsistency.ql b/ruby/ql/consistency-queries/SsaConsistency.ql index 30503e6aa1fb..235eac263442 100644 --- a/ruby/ql/consistency-queries/SsaConsistency.ql +++ b/ruby/ql/consistency-queries/SsaConsistency.ql @@ -1,19 +1,3 @@ import codeql.ruby.dataflow.SSA import codeql.ruby.dataflow.internal.SsaImpl import Consistency - -class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -class MyRelevantDefinitionExt extends RelevantDefinitionExt, DefinitionExt { - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} diff --git a/rust/ql/consistency-queries/SsaConsistency.ql b/rust/ql/consistency-queries/SsaConsistency.ql index 0764842dac37..51a5f97e9786 100644 --- a/rust/ql/consistency-queries/SsaConsistency.ql +++ b/rust/ql/consistency-queries/SsaConsistency.ql @@ -1,19 +1,3 @@ import codeql.rust.dataflow.Ssa import codeql.rust.dataflow.internal.SsaImpl import Consistency - -class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -class MyRelevantDefinitionExt extends RelevantDefinitionExt, DefinitionExt { - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index b95420375380..2137fc489101 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1350,66 +1350,28 @@ module Make Input> { /** Provides a set of consistency queries. */ module Consistency { - /** A definition that is relevant for the consistency queries. */ - abstract class RelevantDefinition extends Definition { - /** Override this predicate to ensure locations in consistency results. */ - abstract predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - } - - /** A definition that is relevant for the consistency queries. */ - abstract class RelevantDefinitionExt extends DefinitionExt { - /** Override this predicate to ensure locations in consistency results. */ - abstract predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - } - /** Holds if a read can be reached from multiple definitions. */ - query predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) { + query predicate nonUniqueDef(Definition def, SourceVariable v, BasicBlock bb, int i) { ssaDefReachesRead(v, def, bb, i) and not exists(unique(Definition def0 | ssaDefReachesRead(v, def0, bb, i))) } - /** Holds if a read can be reached from multiple definitions. */ - query predicate nonUniqueDefExt( - RelevantDefinitionExt def, SourceVariable v, BasicBlock bb, int i - ) { - ssaDefReachesReadExt(v, def, bb, i) and - not exists(unique(DefinitionExt def0 | ssaDefReachesReadExt(v, def0, bb, i))) - } - /** Holds if a read cannot be reached from a definition. */ query predicate readWithoutDef(SourceVariable v, BasicBlock bb, int i) { variableRead(bb, i, v, _) and not ssaDefReachesRead(v, _, bb, i) } - /** Holds if a read cannot be reached from a definition. */ - query predicate readWithoutDefExt(SourceVariable v, BasicBlock bb, int i) { - variableRead(bb, i, v, _) and - not ssaDefReachesReadExt(v, _, bb, i) - } - /** Holds if a definition cannot reach a read. */ - query predicate deadDef(RelevantDefinition def, SourceVariable v) { + query predicate deadDef(Definition def, SourceVariable v) { v = def.getSourceVariable() and not ssaDefReachesRead(_, def, _, _) and not phiHasInputFromBlock(_, def, _) and not uncertainWriteDefinitionInput(_, def) } - /** Holds if a definition cannot reach a read. */ - query predicate deadDefExt(RelevantDefinitionExt def, SourceVariable v) { - v = def.getSourceVariable() and - not ssaDefReachesReadExt(_, def, _, _) and - not phiHasInputFromBlockExt(_, def, _) and - not uncertainWriteDefinitionInput(_, def) - } - /** Holds if a read is not dominated by a definition. */ - query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) { + query predicate notDominatedByDef(Definition def, SourceVariable v, BasicBlock bb, int i) { exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) | ssaDefReachesReadWithinBlock(v, def, bb, i) and (bb != bbDef or i < iDef) @@ -1419,20 +1381,6 @@ module Make Input> { not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _) ) } - - /** Holds if a read is not dominated by a definition. */ - query predicate notDominatedByDefExt( - RelevantDefinitionExt def, SourceVariable v, BasicBlock bb, int i - ) { - exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef, _) | - ssaDefReachesReadWithinBlock(v, def, bb, i) and - (bb != bbDef or i < iDef) - or - ssaDefReachesReadExt(v, def, bb, i) and - not ssaDefReachesReadWithinBlock(v, def, bb, i) and - not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _, _) - ) - } } /** Provides the input to `DataFlowIntegration`. */ From 4c0e5f62cfeb1b69ebd550a16e2bb0d30700c6fa Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 26 Feb 2025 14:43:31 +0100 Subject: [PATCH 09/12] Rust: Remove remaining DefinitionExt references. --- .../codeql/rust/dataflow/internal/SsaImpl.qll | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll index 060393129eae..5780c5b79766 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll @@ -330,33 +330,6 @@ private module Cached { import Cached private import codeql.rust.dataflow.Ssa -/** - * An extended static single assignment (SSA) definition. - * - * This is either a normal SSA definition (`Definition`) or a - * phi-read node (`PhiReadNode`). - * - * Only intended for internal use. - */ -class DefinitionExt extends Impl::DefinitionExt { - CfgNode getARead() { result = getARead(this) } - - override string toString() { result = this.(Ssa::Definition).toString() } - - override Location getLocation() { result = this.(Ssa::Definition).getLocation() } -} - -/** - * A phi-read node. - * - * Only intended for internal use. - */ -class PhiReadNode extends DefinitionExt, Impl::PhiReadNode { - override string toString() { result = "SSA phi read(" + this.getSourceVariable() + ")" } - - override Location getLocation() { result = Impl::PhiReadNode.super.getLocation() } -} - private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig { class Expr extends CfgNodes::AstCfgNode { predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) } From 5a909aa69c8830bd21a858fff9959c251ec5d837 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 26 Feb 2025 14:47:00 +0100 Subject: [PATCH 10/12] C#: Remove remaining DefinitionExt references. --- .../code/csharp/dataflow/internal/SsaImpl.qll | 41 ++----------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll index 72e7d0eb4231..c891bf45bbcd 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -994,9 +994,9 @@ private module Cached { import Cached -private string getSplitString(DefinitionExt def) { +private string getSplitString(Definition def) { exists(ControlFlow::BasicBlock bb, int i, ControlFlow::Node cfn | - def.definesAt(_, bb, i, _) and + def.definesAt(_, bb, i) and result = cfn.(ControlFlow::Nodes::ElementNode).getSplitsString() | cfn = bb.getNode(i) @@ -1006,48 +1006,13 @@ private string getSplitString(DefinitionExt def) { ) } -string getToStringPrefix(DefinitionExt def) { +string getToStringPrefix(Definition def) { result = "[" + getSplitString(def) + "] " or not exists(getSplitString(def)) and result = "" } -/** - * An extended static single assignment (SSA) definition. - * - * This is either a normal SSA definition (`Definition`) or a - * phi-read node (`PhiReadNode`). - * - * Only intended for internal use. - */ -class DefinitionExt extends Impl::DefinitionExt { - override string toString() { result = this.(Ssa::Definition).toString() } - - /** Gets the location of this definition. */ - override Location getLocation() { result = this.(Ssa::Definition).getLocation() } - - /** Gets the enclosing callable of this definition. */ - Callable getEnclosingCallable() { result = this.(Ssa::Definition).getEnclosingCallable() } -} - -/** - * A phi-read node. - * - * Only intended for internal use. - */ -class PhiReadNode extends DefinitionExt, Impl::PhiReadNode { - override string toString() { - result = getToStringPrefix(this) + "SSA phi read(" + this.getSourceVariable() + ")" - } - - override Location getLocation() { result = this.getBasicBlock().getLocation() } - - override Callable getEnclosingCallable() { - result = this.getSourceVariable().getEnclosingCallable() - } -} - private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig { private import csharp as Cs private import semmle.code.csharp.controlflow.BasicBlocks From f5eb2d94bc6485b3aac854c7096557a0d66e1b4b Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 27 Feb 2025 13:07:31 +0100 Subject: [PATCH 11/12] SSA: Use Definition.getLocation in DefinitionExt. --- shared/ssa/codeql/ssa/Ssa.qll | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index 2137fc489101..4602af99d70f 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1256,11 +1256,7 @@ module Make Input> { string toString() { result = this.(Definition).toString() } /** Gets the location of this SSA definition. */ - Location getLocation() { - exists(BasicBlock bb, int i | this.definesAt(_, bb, i, _) | - if i = -1 then result = bb.getLocation() else result = bb.getNode(i).getLocation() - ) - } + Location getLocation() { result = this.(Definition).getLocation() } } /** @@ -1346,6 +1342,8 @@ module Make Input> { */ class PhiReadNode extends DefinitionExt, TPhiReadNode { override string toString() { result = "SSA phi read(" + this.getSourceVariable() + ")" } + + override Location getLocation() { result = this.getBasicBlock().getLocation() } } /** Provides a set of consistency queries. */ From 6c896026913237ee510d77d289d30019d8c1792a Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 27 Feb 2025 13:30:08 +0100 Subject: [PATCH 12/12] SSA: Add some qldoc. --- shared/ssa/codeql/ssa/Ssa.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index 4602af99d70f..5bdc2b8a28ad 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1610,6 +1610,7 @@ module Make Input> { SsaDefinitionExtNodeImpl() { this = TSsaDefinitionNode(def) } + /** Gets the corresponding `DefinitionExt`. */ DefinitionExt getDefExt() { result = def } deprecated override DefinitionExt getDefinitionExt() { result = def } @@ -1925,6 +1926,7 @@ module Make Input> { this = TRefPhiRead(_, result) } + /** Holds if this reference is a synthesized phi read. */ predicate isPhiRead() { this = TRefPhiRead(_, _) } /** Gets a textual representation of this SSA reference. */