Skip to content

Commit f1add38

Browse files
committed
Synthesise writes to self for classes/modules
This requires changing the CFG trees for classes and modules from post-order to pre-order so that we can place the writes at the root node of the tree, to prevent them overlapping with reads in the body of the class/module. We need to do this because classes and modules don't define their own basic block, but re-use the surrounding one. This problem doesn't occur for `self` variables in methods because each method has its own basic block and we can place the write on the entry node of the bock.
1 parent 356828c commit f1add38

File tree

4 files changed

+44
-28
lines changed

4 files changed

+44
-28
lines changed

ruby/ql/lib/codeql/ruby/ast/Statement.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class Stmt extends AstNode, TStmt {
1414
/** Gets a control-flow node for this statement, if any. */
1515
CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this }
1616

17+
/** Gets a control-flow entry node for this statement, if any */
18+
AstNode getAControlFlowEntryNode() { result = getAControlFlowEntryNode(this) }
19+
1720
/** Gets the control-flow scope of this statement, if any. */
1821
CfgScope getCfgScope() { result = getCfgScope(this) }
1922

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -808,19 +808,28 @@ module Trees {
808808
}
809809
}
810810

811+
/**
812+
* Namespaces (i.e. modules or classes) behave like other `BodyStmt`s except they are
813+
* executed in pre-order rather than post-order. We do this in order to insert a write for
814+
* `self` before any subsequent reads in the namespace body.
815+
*/
811816
private class NamespaceTree extends BodyStmtTree, Namespace {
812-
final override predicate first(AstNode first) {
813-
this.firstInner(first)
814-
or
815-
not exists(this.getAChild(_)) and
816-
first = this
817-
}
817+
final override predicate first(AstNode first) { first = this }
818818

819819
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
820820
BodyStmtTree.super.succ(pred, succ, c)
821821
or
822-
succ = this and
823-
this.lastInner(pred, c)
822+
pred = this and
823+
this.firstInner(succ) and
824+
c instanceof SimpleCompletion
825+
}
826+
827+
final override predicate last(AstNode last, Completion c) {
828+
this.lastInner(last, c)
829+
or
830+
not exists(this.getAChild(_)) and
831+
last = this and
832+
c.isValidFor(this)
824833
}
825834
}
826835

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@ class SourceVariable = LocalVariable;
2323
*/
2424
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
2525
(
26-
// We consider the `self` variable to have a single write at the entry to a method block.
26+
// We consider the `self` variable to have a single write at the entry to a method block...
2727
v.(SelfVariable).getDeclaringScope() = bb.(BasicBlocks::EntryBasicBlock).getScope() and
2828
i = 0
2929
or
30+
// ...or a class or module block.
31+
bb.getNode(i).getNode() =
32+
v.(SelfVariable).getDeclaringScope().(ModuleBase).getAControlFlowEntryNode()
33+
or
3034
SsaImpl::uninitializedWrite(bb, i, v)
3135
or
3236
SsaImpl::capturedEntryWrite(bb, i, v)

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

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,7 @@ cfg.rb:
10561056
#-----| -> ... = ...
10571057

10581058
# 54| ... = ...
1059-
#-----| -> Object
1059+
#-----| -> Silly
10601060

10611061
# 54| character
10621062
#-----| -> ?\x40
@@ -1065,7 +1065,7 @@ cfg.rb:
10651065
#-----| -> ... = ...
10661066

10671067
# 58| Silly
1068-
#-----| -> x
1068+
#-----| -> Object
10691069

10701070
# 58| Object
10711071
#-----| -> complex
@@ -1293,7 +1293,7 @@ cfg.rb:
12931293
#-----| -> self
12941294

12951295
# 69| print
1296-
#-----| -> Silly
1296+
#-----| -> x
12971297

12981298
# 69| exit print
12991299

@@ -1620,7 +1620,7 @@ cfg.rb:
16201620
#-----| -> #{...}
16211621

16221622
# 113| ... if ...
1623-
#-----| -> @field
1623+
#-----| -> C
16241624

16251625
# 113| call to puts
16261626
#-----| -> ... if ...
@@ -1642,7 +1642,7 @@ cfg.rb:
16421642
#-----| -> ... > ...
16431643

16441644
# 115| C
1645-
#-----| -> swap
1645+
#-----| -> @field
16461646

16471647
# 116| ... = ...
16481648
#-----| -> @@static_field
@@ -1654,7 +1654,7 @@ cfg.rb:
16541654
#-----| -> ... = ...
16551655

16561656
# 117| ... = ...
1657-
#-----| -> C
1657+
#-----| -> swap
16581658

16591659
# 117| @@static_field
16601660
#-----| -> 10
@@ -1663,7 +1663,7 @@ cfg.rb:
16631663
#-----| -> ... = ...
16641664

16651665
# 120| ... = ...
1666-
#-----| -> nothing
1666+
#-----| -> M
16671667

16681668
# 120| swap
16691669
#-----| -> -> { ... }
@@ -1701,7 +1701,7 @@ cfg.rb:
17011701
#-----| -> call to []
17021702

17031703
# 122| M
1704-
#-----| -> EmptyClass
1704+
#-----| -> nothing
17051705

17061706
# 123| ... = ...
17071707
#-----| -> some
@@ -1812,7 +1812,7 @@ cfg.rb:
18121812
#-----| -> #{...}
18131813

18141814
# 130| ... = ...
1815-
#-----| -> M
1815+
#-----| -> EmptyClass
18161816

18171817
# 130| Constant
18181818
#-----| -> 5
@@ -2832,7 +2832,7 @@ desugar.rb:
28322832
#-----| -> __synth__0
28332833

28342834
# 25| m7
2835-
#-----| -> @x
2835+
#-----| -> X
28362836

28372837
# 25| exit m7
28382838

@@ -2939,7 +2939,7 @@ desugar.rb:
29392939
#-----| -> call to []
29402940

29412941
# 29| X
2942-
#-----| -> $global_var
2942+
#-----| -> @x
29432943

29442944
# 30| ... = ...
29452945
#-----| -> @x
@@ -2978,7 +2978,7 @@ desugar.rb:
29782978
#-----| -> @@y
29792979

29802980
# 34| ... = ...
2981-
#-----| -> X
2981+
#-----| -> $global_var
29822982

29832983
# 34| @@y
29842984
#-----| -> 4
@@ -3907,7 +3907,7 @@ loops.rb:
39073907

39083908
raise.rb:
39093909
# 1| enter raise.rb
3910-
#-----| -> Exception
3910+
#-----| -> ExceptionA
39113911

39123912
# 1| ExceptionA
39133913
#-----| -> Exception
@@ -3918,13 +3918,13 @@ raise.rb:
39183918
#-----| -> exit raise.rb
39193919

39203920
# 1| Exception
3921-
#-----| -> ExceptionA
3921+
#-----| -> ExceptionB
39223922

39233923
# 4| ExceptionB
3924-
#-----| -> m1
3924+
#-----| -> Exception
39253925

39263926
# 4| Exception
3927-
#-----| -> ExceptionB
3927+
#-----| -> m1
39283928

39293929
# 7| enter m1
39303930
#-----| -> x
@@ -5070,7 +5070,7 @@ raise.rb:
50705070
#-----| -> self
50715071

50725072
# 158| m15
5073-
#-----| -> self
5073+
#-----| -> C
50745074

50755075
# 158| exit m15
50765076

@@ -5134,13 +5134,13 @@ raise.rb:
51345134
#-----| false -> self
51355135

51365136
# 166| C
5137-
#-----| -> exit raise.rb (normal)
5137+
#-----| -> self
51385138

51395139
# 167| enter m
51405140
#-----| -> self
51415141

51425142
# 167| m
5143-
#-----| -> C
5143+
#-----| -> exit raise.rb (normal)
51445144

51455145
# 167| exit m
51465146

0 commit comments

Comments
 (0)