Skip to content

Commit a908f2f

Browse files
authored
Merge pull request #121 from github/aibaars/dataflow-2
Dataflow: identify ReturnNodes
2 parents 6c63bd2 + 4f3412f commit a908f2f

5 files changed

Lines changed: 111 additions & 13 deletions

File tree

ql/src/codeql_ruby/dataflow/internal/DataFlowPrivate.qll

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,15 @@ private module Cached {
132132
or
133133
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::CaseExprCfgNode).getBranch(_)
134134
or
135-
exists(CfgNodes::ExprCfgNode exprTo, ExprReturnNode n |
135+
exists(CfgNodes::ExprCfgNode exprTo, ReturningStatementNode n |
136136
nodeFrom = n and
137137
exprTo = nodeTo.asExpr() and
138-
n.getKind() instanceof BreakReturnKind and
138+
n.getReturningNode().getNode() instanceof BreakStmt and
139139
exprTo.getNode() instanceof Loop and
140-
nodeTo.asExpr().getAPredecessor(any(SuccessorTypes::BreakSuccessor s)) = n.getExprNode()
140+
nodeTo.asExpr().getAPredecessor(any(SuccessorTypes::BreakSuccessor s)) = n.getReturningNode()
141141
)
142142
or
143-
nodeFrom.asExpr() = nodeTo.(ExprReturnNode).getExprNode().getReturnedValueNode()
143+
nodeFrom.asExpr() = nodeTo.(ReturningStatementNode).getReturningNode().getReturnedValueNode()
144144
or
145145
nodeTo.asExpr() =
146146
any(CfgNodes::ExprNodes::ForExprCfgNode for |
@@ -182,6 +182,28 @@ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode {
182182
override string toStringImpl() { result = def.toString() }
183183
}
184184

185+
/**
186+
* A value returning statement, viewed as a node in a data flow graph.
187+
*
188+
* Note that because of control-flow splitting, one `ReturningStmt` may correspond
189+
* to multiple `ReturningStatementNode`s, just like it may correspond to multiple
190+
* `ControlFlow::Node`s.
191+
*/
192+
class ReturningStatementNode extends NodeImpl, TReturningNode {
193+
private CfgNodes::ReturningCfgNode n;
194+
195+
ReturningStatementNode() { this = TReturningNode(n) }
196+
197+
/** Gets the expression corresponding to this node. */
198+
CfgNodes::ReturningCfgNode getReturningNode() { result = n }
199+
200+
override CfgScope getCfgScope() { result = n.getScope() }
201+
202+
override Location getLocationImpl() { result = n.getLocation() }
203+
204+
override string toStringImpl() { result = n.toString() }
205+
}
206+
185207
private module ParameterNodes {
186208
abstract private class ParameterNodeImpl extends ParameterNode, NodeImpl { }
187209

@@ -241,29 +263,49 @@ abstract class ReturnNode extends Node {
241263
}
242264

243265
private module ReturnNodes {
266+
private predicate isValid(CfgNodes::ReturningCfgNode node) {
267+
exists(ReturningStmt stmt, Callable scope |
268+
stmt = node.getNode() and
269+
scope = node.getScope()
270+
|
271+
stmt instanceof ReturnStmt and
272+
(scope instanceof Method or scope instanceof SingletonMethod or scope instanceof Lambda)
273+
or
274+
stmt instanceof NextStmt and
275+
(scope instanceof Block or scope instanceof Lambda)
276+
or
277+
stmt instanceof BreakStmt and
278+
(scope instanceof Block or scope instanceof Lambda)
279+
)
280+
}
281+
244282
/**
245283
* A data-flow node that represents an expression returned by a callable,
246284
* either using an explict `return` statement or as the expression of a method body.
247285
*/
248-
class ExprReturnNode extends ReturnNode, NodeImpl, TReturningNode {
286+
class ExplicitReturnNode extends ReturnNode, ReturningStatementNode {
249287
private CfgNodes::ReturningCfgNode n;
250288

251-
ExprReturnNode() { this = TReturningNode(n) }
252-
253-
/** Gets the statement corresponding to this node. */
254-
CfgNodes::ReturningCfgNode getExprNode() { result = n }
289+
ExplicitReturnNode() {
290+
isValid(this.getReturningNode()) and
291+
n.getASuccessor().(CfgNodes::AnnotatedExitNode).isNormal() and
292+
n.getScope() instanceof Callable
293+
}
255294

256295
override ReturnKind getKind() {
257296
if n.getNode() instanceof BreakStmt
258297
then result instanceof BreakReturnKind
259298
else result instanceof NormalReturnKind
260299
}
300+
}
261301

262-
override CfgScope getCfgScope() { result = n.getScope() }
263-
264-
override Location getLocationImpl() { result = n.getLocation() }
302+
class ExprReturnNode extends ReturnNode, ExprNode {
303+
ExprReturnNode() {
304+
this.getExprNode().getASuccessor().(CfgNodes::AnnotatedExitNode).isNormal() and
305+
this.getEnclosingCallable() instanceof Callable
306+
}
265307

266-
override string toStringImpl() { result = n.toString() }
308+
override ReturnKind getKind() { result instanceof NormalReturnKind }
267309
}
268310
}
269311

ql/test/library-tests/dataflow/local/DataflowStep.expected

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,18 @@
3131
| local_dataflow.rb:20:17:20:21 | break | local_dataflow.rb:19:1:21:3 | for ... in ... |
3232
| local_dataflow.rb:24:2:24:8 | break | local_dataflow.rb:23:1:25:3 | while ... |
3333
| local_dataflow.rb:24:8:24:8 | 5 | local_dataflow.rb:24:2:24:8 | break |
34+
| local_dataflow.rb:28:5:28:26 | M | local_dataflow.rb:28:1:28:26 | ... = ... |
35+
| local_dataflow.rb:28:15:28:22 | module | local_dataflow.rb:28:5:28:26 | M |
36+
| local_dataflow.rb:30:5:30:24 | C | local_dataflow.rb:30:1:30:24 | ... = ... |
37+
| local_dataflow.rb:30:14:30:20 | class | local_dataflow.rb:30:5:30:24 | C |
38+
| local_dataflow.rb:32:5:32:25 | bar | local_dataflow.rb:32:1:32:25 | ... = ... |
39+
| local_dataflow.rb:32:5:32:25 | bar | local_dataflow.rb:32:1:32:25 | ... = ... |
40+
| local_dataflow.rb:34:7:34:7 | x | local_dataflow.rb:35:6:35:6 | x |
41+
| local_dataflow.rb:36:13:36:13 | 7 | local_dataflow.rb:36:6:36:13 | return |
42+
| local_dataflow.rb:41:7:41:7 | x | local_dataflow.rb:42:6:42:6 | x |
43+
| local_dataflow.rb:43:13:43:13 | 7 | local_dataflow.rb:43:6:43:13 | return |
44+
| local_dataflow.rb:45:10:45:10 | 6 | local_dataflow.rb:45:3:45:10 | return |
45+
| local_dataflow.rb:49:3:53:3 | <captured> | local_dataflow.rb:50:18:50:18 | x |
46+
| local_dataflow.rb:50:8:50:13 | next | local_dataflow.rb:50:3:50:13 | next |
47+
| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:51:20:51:20 | x |
48+
| local_dataflow.rb:51:9:51:15 | break | local_dataflow.rb:51:3:51:15 | break |
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
| local_dataflow.rb:6:3:6:14 | ... = ... |
2+
| local_dataflow.rb:32:14:32:21 | method |
3+
| local_dataflow.rb:36:6:36:13 | return |
4+
| local_dataflow.rb:38:3:38:13 | reachable |
5+
| local_dataflow.rb:43:6:43:13 | return |
6+
| local_dataflow.rb:45:3:45:10 | return |
7+
| local_dataflow.rb:50:3:50:13 | next |
8+
| local_dataflow.rb:51:3:51:15 | break |
9+
| local_dataflow.rb:52:3:52:10 | normal |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import ruby
2+
import codeql_ruby.dataflow.internal.DataFlowPrivate
3+
4+
select any(ReturnNode node)

ql/test/library-tests/dataflow/local/local_dataflow.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,31 @@ def foo(a)
2323
while true
2424
break 5
2525
end
26+
27+
# string flows to x
28+
x = module M; "module" end
29+
# string flows to x
30+
x = class C; "class" end
31+
# string does not flow to x because "def" evaluates to a method symbol
32+
x = def bar; "method" end
33+
34+
def m x
35+
if x == 4
36+
return 7
37+
end
38+
"reachable"
39+
end
40+
41+
def m x
42+
if x == 4
43+
return 7
44+
end
45+
return 6
46+
"unreachable"
47+
end
48+
49+
m do
50+
next "next" if x < 4
51+
break "break" if x < 9
52+
"normal"
53+
end

0 commit comments

Comments
 (0)