Skip to content

Commit dabf77c

Browse files
committed
Ruby: Include content flow through captured variables
1 parent 8196f03 commit dabf77c

File tree

6 files changed

+222
-64
lines changed

6 files changed

+222
-64
lines changed

ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,21 @@ module ExprNodes {
693693

694694
override VariableReadAccess e;
695695

696-
final override VariableReadAccess getExpr() { result = ExprCfgNode.super.getExpr() }
696+
override VariableReadAccess getExpr() { result = ExprCfgNode.super.getExpr() }
697+
698+
/** Gets the variable that is being accessed. */
699+
Variable getVariable() { result = this.getExpr().getVariable() }
700+
}
701+
702+
/** A control-flow node that wraps a `LocalVariableReadAccess` AST expression. */
703+
class LocalVariableReadAccessCfgNode extends VariableReadAccessCfgNode {
704+
override string getAPrimaryQlClass() { result = "LocalVariableReadAccessCfgNode" }
705+
706+
override LocalVariableReadAccess e;
707+
708+
final override LocalVariableReadAccess getExpr() { result = super.getExpr() }
709+
710+
final override LocalVariable getVariable() { result = super.getVariable() }
697711
}
698712

699713
private class InstanceVariableAccessMapping extends ExprChildMapping, InstanceVariableAccess {
@@ -733,7 +747,21 @@ module ExprNodes {
733747

734748
override VariableWriteAccess e;
735749

736-
final override VariableWriteAccess getExpr() { result = ExprCfgNode.super.getExpr() }
750+
override VariableWriteAccess getExpr() { result = ExprCfgNode.super.getExpr() }
751+
752+
/** Gets the variable that is being accessed. */
753+
Variable getVariable() { result = this.getExpr().getVariable() }
754+
}
755+
756+
/** A control-flow node that wraps a `LocalVariableWriteAccess` AST expression. */
757+
class LocalVariableWriteAccessCfgNode extends VariableWriteAccessCfgNode {
758+
override string getAPrimaryQlClass() { result = "LocalVariableReadAccessCfgNode" }
759+
760+
override LocalVariableWriteAccess e;
761+
762+
final override LocalVariableWriteAccess getExpr() { result = super.getExpr() }
763+
764+
final override LocalVariable getVariable() { result = super.getVariable() }
737765
}
738766

739767
/** A control-flow node that wraps a `ConstantReadAccess` AST expression. */

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,22 @@ CfgNodes::ExprCfgNode getAPostUpdateNodeForArg(Argument arg) {
7171
not exists(getALastEvalNode(result))
7272
}
7373

74+
/**
75+
* Gets the data-flow node referencing SSA definition `def` at index `i` in basic
76+
* block `bb`. The node is either the definition itself, or a read of the
77+
* definition.
78+
*/
79+
Node getSsaRefNode(Ssa::Definition def, BasicBlock bb, int i) {
80+
def = result.(SsaDefinitionNode).getDefinition() and
81+
def.definesAt(_, bb, i)
82+
or
83+
exists(CfgNodes::ExprCfgNode e |
84+
e = result.asExpr() and
85+
e = bb.getNode(i) and
86+
e = def.getARead()
87+
)
88+
}
89+
7490
/** Provides predicates related to local data flow. */
7591
module LocalFlow {
7692
private import codeql.ruby.dataflow.internal.SsaImpl
@@ -80,15 +96,9 @@ module LocalFlow {
8096
* can reach `next`.
8197
*/
8298
private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) {
83-
exists(BasicBlock bb, int i | lastRefBeforeRedef(def, bb, i, next) |
84-
def = nodeFrom.(SsaDefinitionNode).getDefinition() and
85-
def.definesAt(_, bb, i)
86-
or
87-
exists(CfgNodes::ExprCfgNode e |
88-
e = nodeFrom.asExpr() and
89-
e = bb.getNode(i) and
90-
e.getExpr() instanceof VariableReadAccess
91-
)
99+
exists(BasicBlock bb, int i |
100+
lastRefBeforeRedef(def, bb, i, next) and
101+
nodeFrom = getSsaRefNode(def, bb, i)
92102
)
93103
}
94104

@@ -142,18 +152,15 @@ module LocalFlow {
142152
// Flow into phi node
143153
exists(Ssa::PhiNode phi |
144154
localFlowSsaInput(nodeFrom, def, phi) and
145-
phi = nodeTo.(SsaDefinitionNode).getDefinition() and
146-
def = phi.getAnInput()
155+
phi = nodeTo.(SsaDefinitionNode).getDefinition()
147156
)
157+
// or
158+
// // Flow into uncertain SSA definition
159+
// exists(SsaImpl::UncertainWriteDefinition uncertain |
160+
// localFlowSsaInput(nodeFrom, def, uncertain) and
161+
// uncertain = nodeTo.(SsaDefinitionNode).getDefinition()
162+
// )
148163
)
149-
// TODO
150-
// or
151-
// // Flow into uncertain SSA definition
152-
// exists(LocalFlow::UncertainExplicitSsaDefinition uncertain |
153-
// localFlowSsaInput(nodeFrom, def, uncertain) and
154-
// uncertain = nodeTo.(SsaDefinitionNode).getDefinition() and
155-
// def = uncertain.getPriorDefinition()
156-
// )
157164
}
158165

159166
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
@@ -996,12 +1003,19 @@ private module OutNodes {
9961003
import OutNodes
9971004

9981005
predicate jumpStep(Node pred, Node succ) {
999-
SsaImpl::captureFlowIn(_, pred.(SsaDefinitionNode).getDefinition(),
1000-
succ.(SsaDefinitionNode).getDefinition())
1006+
// Flow into a method via a captured variable
1007+
exists(Ssa::Definition def, BasicBlock bb, int i |
1008+
SsaImpl::captureFlowIn(_, def, bb, i, succ.(SsaDefinitionNode).getDefinition()) and
1009+
[pred, pred.(PostUpdateNode).getPreUpdateNode()] = getSsaRefNode(def, bb, i)
1010+
)
10011011
or
1012+
// Flow out of a method directly via a captured variable
10021013
SsaImpl::captureFlowOut(_, pred.(SsaDefinitionNode).getDefinition(),
10031014
succ.(SsaDefinitionNode).getDefinition())
10041015
or
1016+
// Flow out of a method via content on a captured variable
1017+
SsaImpl::captureContentFlowOut(_, pred.(PostUpdateNode).getPreUpdateNode().asExpr(), succ.asExpr())
1018+
or
10051019
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
10061020
}
10071021

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
468468
|
469469
def.getARead() = testedNode and
470470
guardChecks(g, testedNode, branch) and
471-
SsaImpl::captureFlowIn(call, def, result) and
471+
SsaImpl::captureFlowIn(call, def, _, _, result) and
472472
guardControlsBlock(g, call.getBasicBlock(), branch) and
473473
result.getBasicBlock().getScope() = call.getExpr().(MethodCall).getBlock()
474474
)
@@ -533,7 +533,7 @@ abstract deprecated class BarrierGuard extends CfgNodes::ExprCfgNode {
533533
|
534534
def.getARead() = testedNode and
535535
this.checks(testedNode, branch) and
536-
SsaImpl::captureFlowIn(call, def, result) and
536+
SsaImpl::captureFlowIn(call, def, _, _, result) and
537537
this.controlsBlock(call.getBasicBlock(), branch) and
538538
result.getBasicBlock().getScope() = call.getExpr().(MethodCall).getBlock()
539539
)

0 commit comments

Comments
 (0)