Skip to content

Commit 634c8cd

Browse files
committed
Ruby: Generalize CfgNodes::ChildMapping
1 parent fcec8a8 commit 634c8cd

File tree

2 files changed

+92
-24
lines changed

2 files changed

+92
-24
lines changed

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

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -149,25 +149,15 @@ private AstNode desugar(AstNode n) {
149149
/**
150150
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
151151
*/
152-
abstract private class ExprChildMapping extends Expr {
152+
abstract private class ChildMapping extends AstNode {
153153
/**
154154
* Holds if `child` is a (possibly nested) child of this expression
155155
* for which we would like to find a matching CFG child.
156156
*/
157157
abstract predicate relevantChild(AstNode child);
158158

159159
pragma[nomagic]
160-
private predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) {
161-
this.relevantChild(child) and
162-
cfn = this.getAControlFlowNode() and
163-
bb.getANode() = cfn
164-
or
165-
exists(BasicBlock mid |
166-
this.reachesBasicBlock(child, cfn, mid) and
167-
bb = mid.getAPredecessor() and
168-
not mid.getANode().getNode() = child
169-
)
170-
}
160+
abstract predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb);
171161

172162
/**
173163
* Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn`
@@ -183,6 +173,44 @@ abstract private class ExprChildMapping extends Expr {
183173
}
184174
}
185175

176+
/**
177+
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
178+
*/
179+
abstract private class ExprChildMapping extends Expr, ChildMapping {
180+
pragma[nomagic]
181+
override predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) {
182+
this.relevantChild(child) and
183+
cfn = this.getAControlFlowNode() and
184+
bb.getANode() = cfn
185+
or
186+
exists(BasicBlock mid |
187+
this.reachesBasicBlock(child, cfn, mid) and
188+
bb = mid.getAPredecessor() and
189+
not mid.getANode().getNode() = child
190+
)
191+
}
192+
}
193+
194+
/**
195+
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
196+
*/
197+
abstract private class NonExprChildMapping extends ChildMapping {
198+
NonExprChildMapping() { not this instanceof Expr }
199+
200+
pragma[nomagic]
201+
override predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) {
202+
this.relevantChild(child) and
203+
cfn.getNode() = this and
204+
bb.getANode() = cfn
205+
or
206+
exists(BasicBlock mid |
207+
this.reachesBasicBlock(child, cfn, mid) and
208+
bb = mid.getASuccessor() and
209+
not mid.getANode().getNode() = child
210+
)
211+
}
212+
}
213+
186214
/** Provides classes for control-flow nodes that wrap AST expressions. */
187215
module ExprNodes {
188216
private class LiteralChildMapping extends ExprChildMapping, Literal {
@@ -346,17 +374,17 @@ module ExprNodes {
346374
final ExprCfgNode getBlock() { e.hasCfgChild(e.(MethodCall).getBlock(), this, result) }
347375
}
348376

349-
private class CaseExprChildMapping extends ExprChildMapping, CaseExpr {
350-
override predicate relevantChild(AstNode e) { e = this.getValue() }
351-
}
352-
353377
/** A control-flow node that wraps a `MethodCall` AST expression. */
354378
class MethodCallCfgNode extends CallCfgNode {
355379
MethodCallCfgNode() { super.getExpr() instanceof MethodCall }
356380

357381
override MethodCall getExpr() { result = super.getExpr() }
358382
}
359383

384+
private class CaseExprChildMapping extends ExprChildMapping, CaseExpr {
385+
override predicate relevantChild(AstNode e) { e = this.getValue() or e = this.getABranch() }
386+
}
387+
360388
/** A control-flow node that wraps a `CaseExpr` AST expression. */
361389
class CaseExprCfgNode extends ExprCfgNode {
362390
override CaseExprChildMapping e;
@@ -365,6 +393,47 @@ module ExprNodes {
365393

366394
/** Gets the expression being compared, if any. */
367395
final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) }
396+
397+
/**
398+
* Gets the `n`th branch of this case expression, either a `when` clause, an `in` clause, or an `else` branch.
399+
*/
400+
final AstCfgNode getBranch(int n) { e.hasCfgChild(e.getBranch(n), this, result) }
401+
}
402+
403+
private class InClauseChildMapping extends NonExprChildMapping, InClause {
404+
override predicate relevantChild(AstNode e) {
405+
e = this.getPattern() or
406+
e = this.getCondition() or
407+
e = this.getBody()
408+
}
409+
}
410+
411+
/** A control-flow node that wraps an `InClause` AST expression. */
412+
class InClauseCfgNode extends AstCfgNode {
413+
private InClauseChildMapping e;
414+
415+
/**
416+
* Gets the pattern in this `in`-clause.
417+
*/
418+
final AstCfgNode getPattern() { e.hasCfgChild(e.getPattern(), this, result) }
419+
420+
/** Gets the pattern guard condition in this `in` clause, if any. */
421+
final ExprCfgNode getCondition() { e.hasCfgChild(e.getCondition(), this, result) }
422+
423+
/** Gets the body of this `in`-clause. */
424+
final ExprCfgNode getBody() { e.hasCfgChild(e.getBody(), this, result) }
425+
}
426+
427+
private class WhenClauseChildMapping extends NonExprChildMapping, WhenClause {
428+
override predicate relevantChild(AstNode e) { e = this.getBody() }
429+
}
430+
431+
/** A control-flow node that wraps a `WhenClause` AST expression. */
432+
class WhenClauseCfgNode extends AstCfgNode {
433+
private WhenClauseChildMapping e;
434+
435+
/** Gets the body of this `when`-clause. */
436+
final ExprCfgNode getBody() { e.hasCfgChild(e.getBody(), this, result) }
368437
}
369438

370439
private class ConditionalExprChildMapping extends ExprChildMapping, ConditionalExpr {

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,14 @@ module LocalFlow {
126126
or
127127
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ConditionalExprCfgNode).getBranch(_)
128128
or
129-
exists(CfgNode n, Stmt stmt, CaseExpr c |
130-
c = nodeTo.asExpr().getExpr() and
131-
n = nodeFrom.asExpr() and
132-
n = nodeTo.asExpr().getAPredecessor() and
133-
stmt = n.getNode()
129+
exists(CfgNodes::AstCfgNode branch |
130+
branch = nodeTo.asExpr().(CfgNodes::ExprNodes::CaseExprCfgNode).getBranch(_)
134131
|
135-
stmt = c.getElseBranch() or
136-
stmt = c.getABranch().(InClause).getBody() or
137-
stmt = c.getABranch().(WhenClause).getBody()
132+
nodeFrom.asExpr() = branch.(CfgNodes::ExprNodes::InClauseCfgNode).getBody()
133+
or
134+
nodeFrom.asExpr() = branch.(CfgNodes::ExprNodes::WhenClauseCfgNode).getBody()
135+
or
136+
nodeFrom.asExpr() = branch and branch instanceof CfgNodes::ExprCfgNode
138137
)
139138
or
140139
exists(CfgNodes::ExprCfgNode exprTo, ReturningStatementNode n |

0 commit comments

Comments
 (0)