Skip to content

Commit 89e6c0e

Browse files
committed
CFG: Model calls that may raise an exception
In order to avoid dead `rescue`s, we assume that any call that happens in a `rescue`/`ensure` context may raise an exception.
1 parent 2d08b01 commit 89e6c0e

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

ql/lib/codeql/ruby/controlflow/internal/Completion.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ private predicate completionIsValidForStmt(AstNode n, Completion c) {
7070
c = TReturnCompletion()
7171
}
7272

73+
/**
74+
* Holds if `c` happens in an exception-aware context, that is, it may be
75+
* `rescue`d or `ensure`d. In such cases, we assume that the target of `c`
76+
* may raise an exception (in addition to evaluating normally).
77+
*/
78+
private predicate mayRaise(Call c) {
79+
exists(Trees::BodyStmtTree bst | c = bst.getBodyChild(_, true).getAChild*() |
80+
exists(bst.getARescue())
81+
or
82+
exists(bst.getEnsure())
83+
)
84+
}
85+
7386
/** A completion of a statement or an expression. */
7487
abstract class Completion extends TCompletion {
7588
/** Holds if this completion is valid for node `n`. */
@@ -91,6 +104,9 @@ abstract class Completion extends TCompletion {
91104
or
92105
n = any(RescueModifierExpr parent).getBody() and this = TRaiseCompletion()
93106
or
107+
mayRaise(n) and
108+
this = TRaiseCompletion()
109+
or
94110
not n instanceof NonReturningCall and
95111
not completionIsValidForStmt(n, _) and
96112
not mustHaveBooleanCompletion(n) and

ql/test/library-tests/controlflow/graph/Cfg.expected

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ break_ensure.rb:
182182

183183
# 27| exit m3
184184

185+
# 27| exit m3 (abnormal)
186+
#-----| -> exit m3
187+
185188
# 27| exit m3 (normal)
186189
#-----| -> exit m3
187190

@@ -194,6 +197,7 @@ break_ensure.rb:
194197
# 29| call to nil?
195198
#-----| false -> if ...
196199
#-----| true -> return
200+
#-----| raise -> [ensure: raise] elements
197201

198202
# 29| elements
199203
#-----| -> call to nil?
@@ -204,76 +208,114 @@ break_ensure.rb:
204208
# 32| ensure ...
205209
#-----| -> self
206210

211+
# 32| [ensure: raise] ensure ...
212+
#-----| raise -> exit m3 (abnormal)
213+
207214
# 32| [ensure: return] ensure ...
208215
#-----| return -> exit m3 (normal)
209216

210217
# 33| for ... in ...
211218
#-----| -> ensure ...
212219

220+
# 33| [ensure: raise] for ... in ...
221+
#-----| -> [ensure: raise] ensure ...
222+
213223
# 33| [ensure: return] for ... in ...
214224
#-----| -> [ensure: return] ensure ...
215225

216226
# 33| element
217227
#-----| -> self
218228

229+
# 33| [ensure: raise] element
230+
#-----| -> [ensure: raise] self
231+
219232
# 33| [ensure: return] element
220233
#-----| -> [ensure: return] self
221234

222235
# 33| In
223236
#-----| empty -> for ... in ...
224237
#-----| non-empty -> element
225238

239+
# 33| [ensure: raise] In
240+
#-----| empty -> [ensure: raise] for ... in ...
241+
#-----| non-empty -> [ensure: raise] element
242+
226243
# 33| [ensure: return] In
227244
#-----| empty -> [ensure: return] for ... in ...
228245
#-----| non-empty -> [ensure: return] element
229246

230247
# 33| elements
231248
#-----| -> In
232249

250+
# 33| [ensure: raise] elements
251+
#-----| -> [ensure: raise] In
252+
233253
# 33| [ensure: return] elements
234254
#-----| -> [ensure: return] In
235255

236256
# 33| do ...
237257
#-----| -> In
238258

259+
# 33| [ensure: raise] do ...
260+
#-----| -> [ensure: raise] In
261+
239262
# 33| [ensure: return] do ...
240263
#-----| -> [ensure: return] In
241264

242265
# 35| if ...
243266
#-----| -> do ...
244267

268+
# 35| [ensure: raise] if ...
269+
#-----| -> [ensure: raise] do ...
270+
245271
# 35| [ensure: return] if ...
246272
#-----| -> [ensure: return] do ...
247273

248274
# 35| ... > ...
249275
#-----| true -> break
250276
#-----| false -> if ...
251277

278+
# 35| [ensure: raise] ... > ...
279+
#-----| true -> [ensure: raise] break
280+
#-----| false -> [ensure: raise] if ...
281+
252282
# 35| [ensure: return] ... > ...
253283
#-----| true -> [ensure: return] break
254284
#-----| false -> [ensure: return] if ...
255285

256286
# 35| call to x
257287
#-----| -> 0
258288

289+
# 35| [ensure: raise] call to x
290+
#-----| -> [ensure: raise] 0
291+
259292
# 35| [ensure: return] call to x
260293
#-----| -> [ensure: return] 0
261294

262295
# 35| self
263296
#-----| -> call to x
264297

298+
# 35| [ensure: raise] self
299+
#-----| -> [ensure: raise] call to x
300+
265301
# 35| [ensure: return] self
266302
#-----| -> [ensure: return] call to x
267303

268304
# 35| 0
269305
#-----| -> ... > ...
270306

307+
# 35| [ensure: raise] 0
308+
#-----| -> [ensure: raise] ... > ...
309+
271310
# 35| [ensure: return] 0
272311
#-----| -> [ensure: return] ... > ...
273312

274313
# 36| break
275314
#-----| break -> for ... in ...
276315

316+
# 36| [ensure: raise] break
317+
#-----| break -> [ensure: raise] for ... in ...
318+
277319
# 36| [ensure: return] break
278320
#-----| break -> [ensure: return] for ... in ...
279321

@@ -4213,6 +4255,7 @@ raise.rb:
42134255

42144256
# 74| call to puts
42154257
#-----| -> self
4258+
#-----| raise -> [ensure: raise] self
42164259

42174260
# 74| self
42184261
#-----| -> "0 <= x <= 2"
@@ -4325,6 +4368,7 @@ raise.rb:
43254368

43264369
# 87| call to puts
43274370
#-----| -> self
4371+
#-----| raise -> [ensure: raise] self
43284372

43294373
# 87| self
43304374
#-----| -> "0 <= x <= 2"
@@ -4402,6 +4446,7 @@ raise.rb:
44024446

44034447
# 95| call to puts
44044448
#-----| -> x
4449+
#-----| raise -> [ensure: raise] self
44054450

44064451
# 95| self
44074452
#-----| -> "Begin m9"
@@ -4452,6 +4497,7 @@ raise.rb:
44524497

44534498
# 102| call to puts
44544499
#-----| -> self
4500+
#-----| raise -> [ensure: raise] self
44554501

44564502
# 102| self
44574503
#-----| -> "0 <= x <= 2"
@@ -4470,12 +4516,15 @@ raise.rb:
44704516

44714517
# 104| call to puts
44724518
#-----| -> b1
4519+
#-----| raise -> [ensure: raise] self
44734520

44744521
# 104| [ensure: raise] call to puts
44754522
#-----| -> [ensure: raise] b1
4523+
#-----| raise -> [ensure: raise] self
44764524

44774525
# 104| [ensure: return] call to puts
44784526
#-----| -> [ensure: return] b1
4527+
#-----| raise -> [ensure: raise] self
44794528

44804529
# 104| self
44814530
#-----| -> "outer ensure"
@@ -4563,21 +4612,27 @@ raise.rb:
45634612

45644613
# 110| call to puts
45654614
#-----| -> ensure ...
4615+
#-----| raise -> [ensure: raise] self
45664616

45674617
# 110| [ensure(1): raise] call to puts
45684618
#-----| -> [ensure(1): raise] ensure ...
4619+
#-----| raise -> [ensure: raise] self
45694620

45704621
# 110| [ensure: raise] call to puts
45714622
#-----| -> [ensure: raise] ensure ...
4623+
#-----| raise -> [ensure: raise] self
45724624

45734625
# 110| [ensure: raise, ensure(1): raise] call to puts
45744626
#-----| -> [ensure: raise, ensure(1): raise] ensure ...
4627+
#-----| raise -> [ensure: raise] self
45754628

45764629
# 110| [ensure: return] call to puts
45774630
#-----| -> [ensure: return] ensure ...
4631+
#-----| raise -> [ensure: raise] self
45784632

45794633
# 110| [ensure: return, ensure(1): raise] call to puts
45804634
#-----| -> [ensure: return, ensure(1): raise] ensure ...
4635+
#-----| raise -> [ensure: raise] self
45814636

45824637
# 110| self
45834638
#-----| -> "inner ensure"
@@ -4617,6 +4672,7 @@ raise.rb:
46174672

46184673
# 113| call to puts
46194674
#-----| -> self
4675+
#-----| raise -> [ensure: raise] self
46204676

46214677
# 113| self
46224678
#-----| -> "End m9"

0 commit comments

Comments
 (0)