Merge pull request #19147 from aschackmull/ssa/writedef-source-refactor

Ssa: Refactor data flow integration to make the input signature simpler
This commit is contained in:
Anders Schack-Mulligen
2025-03-31 10:07:09 +02:00
committed by GitHub
11 changed files with 54 additions and 112 deletions

View File

@@ -956,8 +956,6 @@ class GlobalDef extends Definition {
private module SsaImpl = SsaImplCommon::Make<Location, SsaInput>; private module SsaImpl = SsaImplCommon::Make<Location, SsaInput>;
private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationInputSig { private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationInputSig {
private import codeql.util.Void
class Expr extends Instruction { class Expr extends Instruction {
Expr() { Expr() {
exists(IRBlock bb, int i | exists(IRBlock bb, int i |
@@ -977,13 +975,7 @@ private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationI
) )
} }
predicate ssaDefAssigns(SsaImpl::WriteDefinition def, Expr value) { none() } predicate ssaDefHasSource(SsaImpl::WriteDefinition def) { none() }
class Parameter extends Void {
Location getLocation() { none() }
}
predicate ssaDefInitializesParam(SsaImpl::WriteDefinition def, Parameter p) { none() }
predicate allowFlowIntoUncertainDef(SsaImpl::UncertainWriteDefinition def) { any() } predicate allowFlowIntoUncertainDef(SsaImpl::UncertainWriteDefinition def) { any() }

View File

@@ -506,7 +506,7 @@ module SsaFlow {
result.(Impl::ExprPostUpdateNode).getExpr() = result.(Impl::ExprPostUpdateNode).getExpr() =
n.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode() n.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode()
or or
result.(Impl::ParameterNode).getParameter() = n.(ExplicitParameterNode).getSsaDefinition() result.(Impl::WriteDefSourceNode).getDefinition() = n.(ExplicitParameterNode).getSsaDefinition()
} }
predicate localFlowStep(Ssa::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { predicate localFlowStep(Ssa::SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) {

View File

@@ -1023,16 +1023,12 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
Expr getARead(Definition def) { exists(getAReadAtNode(def, result)) } Expr getARead(Definition def) { exists(getAReadAtNode(def, result)) }
predicate ssaDefAssigns(WriteDefinition def, Expr value) { predicate ssaDefHasSource(WriteDefinition def) {
// exclude flow directly from RHS to SSA definition, as we instead want to // exclude flow directly from RHS to SSA definition, as we instead want to
// go from RHS to matching assingnable definition, and from there to SSA definition // go from RHS to matching assignable definition, and from there to SSA definition
none() def instanceof Ssa::ImplicitParameterDefinition
} }
class Parameter = Ssa::ImplicitParameterDefinition;
predicate ssaDefInitializesParam(WriteDefinition def, Parameter p) { def = p }
/** /**
* Allows for flow into uncertain defintions that are not call definitions, * Allows for flow into uncertain defintions that are not call definitions,
* as we, conservatively, consider such definitions to be certain. * as we, conservatively, consider such definitions to be certain.

View File

@@ -26,6 +26,14 @@ private predicate deadcode(Expr e) {
module SsaFlow { module SsaFlow {
module Impl = SsaImpl::DataFlowIntegration; module Impl = SsaImpl::DataFlowIntegration;
private predicate ssaDefAssigns(SsaExplicitUpdate def, Expr value) {
exists(VariableUpdate upd | upd = def.getDefiningExpr() |
value = upd.(VariableAssign).getSource() or
value = upd.(AssignOp) or
value = upd.(RecordBindingVariableExpr)
)
}
Impl::Node asNode(Node n) { Impl::Node asNode(Node n) {
n = TSsaNode(result) n = TSsaNode(result)
or or
@@ -33,7 +41,12 @@ module SsaFlow {
or or
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr() result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
or or
TExplicitParameterNode(result.(Impl::ParameterNode).getParameter()) = n exists(Parameter p |
n = TExplicitParameterNode(p) and
result.(Impl::WriteDefSourceNode).getDefinition().(SsaImplicitInit).isParameterDefinition(p)
)
or
ssaDefAssigns(result.(Impl::WriteDefSourceNode).getDefinition(), n.asExpr())
} }
predicate localFlowStep(SsaSourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { predicate localFlowStep(SsaSourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) {

View File

@@ -647,22 +647,8 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
Expr getARead(Definition def) { result = getAUse(def) } Expr getARead(Definition def) { result = getAUse(def) }
class Parameter = J::Parameter; predicate ssaDefHasSource(WriteDefinition def) {
def instanceof SsaExplicitUpdate or def.(SsaImplicitInit).isParameterDefinition(_)
predicate ssaDefAssigns(Impl::WriteDefinition def, Expr value) {
exists(VariableUpdate upd | upd = def.(SsaExplicitUpdate).getDefiningExpr() |
value = upd.(VariableAssign).getSource() or
value = upd.(AssignOp) or
value = upd.(RecordBindingVariableExpr)
)
}
predicate ssaDefInitializesParam(Impl::WriteDefinition def, Parameter p) {
def.(SsaImplicitInit).getSourceVariable() =
any(SsaSourceVariable v |
v.getVariable() = p and
v.getEnclosingCallable() = p.getCallable()
)
} }
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) { predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {

View File

@@ -56,14 +56,7 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig {
predicate hasCfgNode(js::BasicBlock bb, int i) { this = bb.getNode(i) } predicate hasCfgNode(js::BasicBlock bb, int i) { this = bb.getNode(i) }
} }
predicate ssaDefAssigns(WriteDefinition def, Expr value) { predicate ssaDefHasSource(WriteDefinition def) {
// This library only handles use-use flow after a post-update, there are no definitions, only uses.
none()
}
class Parameter = js::Parameter;
predicate ssaDefInitializesParam(WriteDefinition def, Parameter p) {
// This library only handles use-use flow after a post-update, there are no definitions, only uses. // This library only handles use-use flow after a post-update, there are no definitions, only uses.
none() none()
} }

View File

@@ -108,7 +108,12 @@ module SsaFlow {
or or
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr() result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
or or
n = toParameterNode(result.(Impl::ParameterNode).getParameter()) exists(SsaImpl::ParameterExt p |
n = toParameterNode(p) and
p.isInitializedBy(result.(Impl::WriteDefSourceNode).getDefinition())
)
or
result.(Impl::WriteDefSourceNode).getDefinition().(Ssa::WriteDefinition).assigns(n.asExpr())
} }
predicate localFlowStep( predicate localFlowStep(

View File

@@ -473,20 +473,16 @@ class ParameterExt extends TParameterExt {
private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig { private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig {
private import codeql.ruby.controlflow.internal.Guards as Guards private import codeql.ruby.controlflow.internal.Guards as Guards
class Parameter = ParameterExt;
class Expr extends Cfg::CfgNodes::ExprCfgNode { class Expr extends Cfg::CfgNodes::ExprCfgNode {
predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) } predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) }
} }
Expr getARead(Definition def) { result = Cached::getARead(def) } Expr getARead(Definition def) { result = Cached::getARead(def) }
predicate ssaDefAssigns(WriteDefinition def, Expr value) { predicate ssaDefHasSource(WriteDefinition def) {
def.(Ssa::WriteDefinition).assigns(value) any(ParameterExt p).isInitializedBy(def) or def.(Ssa::WriteDefinition).assigns(_)
} }
predicate ssaDefInitializesParam(WriteDefinition def, Parameter p) { p.isInitializedBy(def) }
class Guard extends Cfg::CfgNodes::AstCfgNode { class Guard extends Cfg::CfgNodes::AstCfgNode {
/** /**
* Holds if the control flow branching from `bb1` is dependent on this guard, * Holds if the control flow branching from `bb1` is dependent on this guard,

View File

@@ -172,10 +172,6 @@ predicate isArgumentForCall(ExprCfgNode arg, CallExprBaseCfgNode call, Parameter
module SsaFlow { module SsaFlow {
private module SsaFlow = SsaImpl::DataFlowIntegration; private module SsaFlow = SsaImpl::DataFlowIntegration;
private ParameterNode toParameterNode(ParamCfgNode p) {
result.(SourceParameterNode).getParameter() = p
}
/** Converts a control flow node into an SSA control flow node. */ /** Converts a control flow node into an SSA control flow node. */
SsaFlow::Node asNode(Node n) { SsaFlow::Node asNode(Node n) {
n = TSsaNode(result) n = TSsaNode(result)
@@ -183,8 +179,6 @@ module SsaFlow {
result.(SsaFlow::ExprNode).getExpr() = n.asExpr() result.(SsaFlow::ExprNode).getExpr() = n.asExpr()
or or
result.(SsaFlow::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr() result.(SsaFlow::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
or
n = toParameterNode(result.(SsaFlow::ParameterNode).getParameter())
} }
predicate localFlowStep( predicate localFlowStep(

View File

@@ -340,10 +340,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
Expr getARead(Definition def) { result = Cached::getARead(def) } Expr getARead(Definition def) { result = Cached::getARead(def) }
/** Holds if SSA definition `def` assigns `value` to the underlying variable. */ predicate ssaDefHasSource(WriteDefinition def) { none() } // handled in `DataFlowImpl.qll` instead
predicate ssaDefAssigns(WriteDefinition def, Expr value) {
none() // handled in `DataFlowImpl.qll` instead
}
private predicate isArg(CfgNodes::CallExprBaseCfgNode call, CfgNodes::ExprCfgNode e) { private predicate isArg(CfgNodes::CallExprBaseCfgNode call, CfgNodes::ExprCfgNode e) {
call.getArgument(_) = e call.getArgument(_) = e
@@ -364,13 +361,6 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
) )
} }
class Parameter = CfgNodes::ParamBaseCfgNode;
/** Holds if SSA definition `def` initializes parameter `p` at function entry. */
predicate ssaDefInitializesParam(WriteDefinition def, Parameter p) {
none() // handled in `DataFlowImpl.qll` instead
}
class Guard extends CfgNodes::AstCfgNode { class Guard extends CfgNodes::AstCfgNode {
/** /**
* Holds if the control flow branching from `bb1` is dependent on this guard, * Holds if the control flow branching from `bb1` is dependent on this guard,

View File

@@ -1459,20 +1459,14 @@ module Make<LocationSig Location, InputSig<Location> Input> {
) )
} }
/** Holds if SSA definition `def` assigns `value` to the underlying variable. */ /**
predicate ssaDefAssigns(WriteDefinition def, Expr value); * Holds if `def` has some form of input flow. For example, the right-hand
* side of an assignment or a parameter of an SSA entry definition.
/** A parameter. */ *
class Parameter { * For such definitions, a flow step is added from a synthetic node
/** Gets a textual representation of this parameter. */ * representing the source to the definition.
string toString(); */
default predicate ssaDefHasSource(WriteDefinition def) { any() }
/** Gets the location of this parameter. */
Location getLocation();
}
/** Holds if SSA definition `def` initializes parameter `p` at function entry. */
predicate ssaDefInitializesParam(WriteDefinition def, Parameter p);
/** /**
* Holds if flow should be allowed into uncertain SSA definition `def` from * Holds if flow should be allowed into uncertain SSA definition `def` from
@@ -1665,17 +1659,8 @@ module Make<LocationSig Location, InputSig<Location> Input> {
cached cached
private newtype TNode = private newtype TNode =
TParamNode(DfInput::Parameter p) { TWriteDefSource(WriteDefinition def) { DfInput::ssaDefHasSource(def) } or
exists(WriteDefinition def | DfInput::ssaDefInitializesParam(def, p)) TExprNode(DfInput::Expr e, Boolean isPost) { e = DfInput::getARead(_) } or
} or
TExprNode(DfInput::Expr e, Boolean isPost) {
e = DfInput::getARead(_)
or
exists(DefinitionExt def |
DfInput::ssaDefAssigns(def, e) and
isPost = false
)
} or
TSsaDefinitionNode(DefinitionExt def) { not phiHasUniqNextNode(def) } or TSsaDefinitionNode(DefinitionExt def) { not phiHasUniqNextNode(def) } or
TSsaInputNode(SsaPhiExt phi, BasicBlock input) { relevantPhiInputNode(phi, input) } TSsaInputNode(SsaPhiExt phi, BasicBlock input) { relevantPhiInputNode(phi, input) }
@@ -1696,21 +1681,21 @@ module Make<LocationSig Location, InputSig<Location> Input> {
final class Node = NodeImpl; final class Node = NodeImpl;
/** A parameter node. */ /** A source of a write definition. */
private class ParameterNodeImpl extends NodeImpl, TParamNode { private class WriteDefSourceNodeImpl extends NodeImpl, TWriteDefSource {
private DfInput::Parameter p; private WriteDefinition def;
ParameterNodeImpl() { this = TParamNode(p) } WriteDefSourceNodeImpl() { this = TWriteDefSource(def) }
/** Gets the underlying parameter. */ /** Gets the underlying definition. */
DfInput::Parameter getParameter() { result = p } WriteDefinition getDefinition() { result = def }
override string toString() { result = p.toString() } override string toString() { result = "[source] " + def.toString() }
override Location getLocation() { result = p.getLocation() } override Location getLocation() { result = def.getLocation() }
} }
final class ParameterNode = ParameterNodeImpl; final class WriteDefSourceNode = WriteDefSourceNodeImpl;
/** A (post-update) expression node. */ /** A (post-update) expression node. */
abstract private class ExprNodePreOrPostImpl extends NodeImpl, TExprNode { abstract private class ExprNodePreOrPostImpl extends NodeImpl, TExprNode {
@@ -1976,12 +1961,8 @@ module Make<LocationSig Location, InputSig<Location> Input> {
*/ */
predicate localFlowStep(SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) { predicate localFlowStep(SourceVariable v, Node nodeFrom, Node nodeTo, boolean isUseStep) {
exists(Definition def | exists(Definition def |
// Flow from assignment into SSA definition // Flow from write definition source into SSA definition
DfInput::ssaDefAssigns(def, nodeFrom.(ExprNode).getExpr()) nodeFrom = TWriteDefSource(def) and
or
// Flow from parameter into entry definition
DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter())
|
isUseStep = false and isUseStep = false and
if DfInput::includeWriteDefsInFlowStep() if DfInput::includeWriteDefsInFlowStep()
then then
@@ -2012,12 +1993,8 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/** Holds if the value of `nodeTo` is given by `nodeFrom`. */ /** Holds if the value of `nodeTo` is given by `nodeFrom`. */
predicate localMustFlowStep(SourceVariable v, Node nodeFrom, Node nodeTo) { predicate localMustFlowStep(SourceVariable v, Node nodeFrom, Node nodeTo) {
exists(Definition def | exists(Definition def |
// Flow from assignment into SSA definition // Flow from write definition source into SSA definition
DfInput::ssaDefAssigns(def, nodeFrom.(ExprNode).getExpr()) nodeFrom = TWriteDefSource(def) and
or
// Flow from parameter into entry definition
DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter())
|
v = def.getSourceVariable() and v = def.getSourceVariable() and
if DfInput::includeWriteDefsInFlowStep() if DfInput::includeWriteDefsInFlowStep()
then nodeTo.(SsaDefinitionNode).getDefinition() = def then nodeTo.(SsaDefinitionNode).getDefinition() = def