Skip to content

Commit f020b2e

Browse files
authored
Merge pull request #335 from github/hmac/self-flow
2 parents 3851a27 + 87df3a0 commit f020b2e

File tree

21 files changed

+824
-211
lines changed

21 files changed

+824
-211
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ class Scope extends AstNode, TScopeType {
2020
result.getName() = name
2121
}
2222
}
23+
24+
class SelfScope extends Scope, TSelfScopeType { }

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/ast/Variable.qll

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class ClassVariable extends Variable instanceof ClassVariableImpl {
7171
final override ClassVariableAccess getAnAccess() { result.getVariable() = this }
7272
}
7373

74+
/** A `self` variable. */
75+
class SelfVariable extends LocalVariable instanceof SelfVariableImpl { }
76+
7477
/** An access to a variable. */
7578
class VariableAccess extends Expr instanceof VariableAccessImpl {
7679
/** Gets the variable this identifier refers to. */
@@ -127,7 +130,7 @@ class VariableReadAccess extends VariableAccess {
127130

128131
/** An access to a local variable. */
129132
class LocalVariableAccess extends VariableAccess instanceof LocalVariableAccessImpl {
130-
final override string getAPrimaryQlClass() { result = "LocalVariableAccess" }
133+
override string getAPrimaryQlClass() { result = "LocalVariableAccess" }
131134

132135
/**
133136
* Holds if this access is a captured variable access. For example in
@@ -144,7 +147,7 @@ class LocalVariableAccess extends VariableAccess instanceof LocalVariableAccessI
144147
* the access to `x` in the first `puts x` is a captured access, while
145148
* the access to `x` in the second `puts x` is not.
146149
*/
147-
final predicate isCapturedAccess() { isCapturedAccess(this) }
150+
predicate isCapturedAccess() { isCapturedAccess(this) }
148151
}
149152

150153
/** An access to a local variable where the value is updated. */
@@ -185,3 +188,17 @@ class ClassVariableWriteAccess extends ClassVariableAccess, VariableWriteAccess
185188

186189
/** An access to a class variable where the value is read. */
187190
class ClassVariableReadAccess extends ClassVariableAccess, VariableReadAccess { }
191+
192+
/** An access to the `self` variable */
193+
class SelfVariableAccess extends LocalVariableAccess instanceof SelfVariableAccessImpl {
194+
final override string getAPrimaryQlClass() { result = "SelfVariableAccess" }
195+
}
196+
197+
/** An access to the `self` variable where the value is read. */
198+
class SelfVariableReadAccess extends SelfVariableAccess, VariableReadAccess {
199+
// We override the definition in `LocalVariableAccess` because it gives the
200+
// wrong result for synthesised `self` variables.
201+
override predicate isCapturedAccess() {
202+
this.getVariable().getDeclaringScope() != this.getCfgScope()
203+
}
204+
}

ruby/ql/lib/codeql/ruby/ast/internal/AST.qll

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,9 @@ private module Cached {
235235
isScopeResolutionMethodCall(g, i)
236236
} or
237237
TSelfReal(Ruby::Self g) or
238-
TSelfSynth(AST::AstNode parent, int i) { mkSynthChild(SelfKind(), parent, i) } or
238+
TSelfSynth(AST::AstNode parent, int i, AST::SelfVariable v) {
239+
mkSynthChild(SelfKind(v), parent, i)
240+
} or
239241
TSimpleParameter(Ruby::Identifier g) { g instanceof Parameter::Range } or
240242
TSimpleSymbolLiteral(Ruby::SimpleSymbol g) or
241243
TSingletonClass(Ruby::SingletonClass g) or
@@ -476,7 +478,7 @@ private module Cached {
476478
or
477479
result = TRShiftExprSynth(parent, i)
478480
or
479-
result = TSelfSynth(parent, i)
481+
result = TSelfSynth(parent, i, _)
480482
or
481483
result = TSplatExprSynth(parent, i)
482484
or
@@ -693,12 +695,16 @@ class TNamedParameter =
693695
class TTuplePattern = TTuplePatternParameter or TDestructuredLeftAssignment or TLeftAssignmentList;
694696

695697
class TVariableAccess =
696-
TLocalVariableAccess or TGlobalVariableAccess or TInstanceVariableAccess or TClassVariableAccess;
698+
TLocalVariableAccess or TGlobalVariableAccess or TInstanceVariableAccess or
699+
TClassVariableAccess or TSelfVariableAccess;
697700

698-
class TLocalVariableAccess = TLocalVariableAccessReal or TLocalVariableAccessSynth;
701+
class TLocalVariableAccess =
702+
TLocalVariableAccessReal or TLocalVariableAccessSynth or TSelfVariableAccess;
699703

700704
class TGlobalVariableAccess = TGlobalVariableAccessReal or TGlobalVariableAccessSynth;
701705

702706
class TInstanceVariableAccess = TInstanceVariableAccessReal or TInstanceVariableAccessSynth;
703707

704708
class TClassVariableAccess = TClassVariableAccessReal or TClassVariableAccessSynth;
709+
710+
class TSelfVariableAccess = TSelfReal or TSelfSynth;

ruby/ql/lib/codeql/ruby/ast/internal/Call.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class IdentifierMethodCall extends MethodCallImpl, TIdentifierMethodCall {
6060

6161
final override string getMethodNameImpl() { result = g.getValue() }
6262

63-
final override AstNode getReceiverImpl() { result = TSelfSynth(this, 0) }
63+
final override AstNode getReceiverImpl() { result = TSelfSynth(this, 0, _) }
6464

6565
final override Expr getArgumentImpl(int n) { none() }
6666

@@ -97,7 +97,7 @@ class RegularMethodCall extends MethodCallImpl, TRegularMethodCall {
9797
not exists(g.getReceiver()) and
9898
toGenerated(result) = g.getMethod().(Ruby::ScopeResolution).getScope()
9999
or
100-
result = TSelfSynth(this, 0)
100+
result = TSelfSynth(this, 0, _)
101101
}
102102

103103
final override string getMethodNameImpl() {

ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ private import codeql.ruby.ast.internal.Parameter
55

66
class TScopeType = TMethodBase or TModuleLike or TBlockLike;
77

8+
/**
9+
* The scope of a `self` variable.
10+
* This differs from a normal scope because it is not affected by blocks or lambdas.
11+
*/
12+
class TSelfScopeType = TMethodBase or TModuleBase;
13+
814
private class TBlockLike = TDoBlock or TLambda or TBlock or TEndBlock;
915

1016
private class TModuleLike = TToplevel or TModuleDeclaration or TClassDeclaration or TSingletonClass;
@@ -29,6 +35,12 @@ module Scope {
2935
result = this.getOuterScope().getEnclosingMethod()
3036
}
3137

38+
SelfBase::Range getEnclosingSelfScope() {
39+
this instanceof SelfBase::Range and result = this
40+
or
41+
not this instanceof SelfBase::Range and result = this.getOuterScope().getEnclosingSelfScope()
42+
}
43+
3244
Range getOuterScope() { result = scopeOf(this) }
3345
}
3446
}
@@ -59,6 +71,15 @@ module ModuleBase {
5971
class Range extends Scope::Range, TypeRange { }
6072
}
6173

74+
module SelfBase {
75+
class TypeRange = MethodBase::TypeRange or ModuleBase::TypeRange;
76+
77+
/**
78+
* A `self` variable can appear in a class, module, method or at the top level.
79+
*/
80+
class Range extends Scope::Range, TypeRange { }
81+
}
82+
6283
pragma[noinline]
6384
private predicate rankHeredocBody(File f, Ruby::HeredocBody b, int i) {
6485
b =

ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ private import TreeSitter
55
private import codeql.ruby.ast.internal.Call
66
private import codeql.ruby.ast.internal.Variable
77
private import codeql.ruby.ast.internal.Pattern
8+
private import codeql.ruby.ast.internal.Scope
89
private import codeql.ruby.AST
910

1011
/** A synthesized AST node kind. */
@@ -34,7 +35,7 @@ newtype SynthKind =
3435
RShiftExprKind() or
3536
SplatExprKind() or
3637
StmtSequenceKind() or
37-
SelfKind() or
38+
SelfKind(SelfVariable v) or
3839
SubExprKind() or
3940
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) }
4041

@@ -142,7 +143,7 @@ private predicate hasLocation(AstNode n, Location l) {
142143
private module ImplicitSelfSynthesis {
143144
pragma[nomagic]
144145
private predicate identifierMethodCallSelfSynthesis(AstNode mc, int i, Child child) {
145-
child = SynthChild(SelfKind()) and
146+
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
146147
mc = TIdentifierMethodCall(_) and
147148
i = 0
148149
}
@@ -163,7 +164,7 @@ private module ImplicitSelfSynthesis {
163164
not exists(g.(Ruby::Call).getReceiver()) and
164165
not exists(g.(Ruby::Call).getMethod().(Ruby::ScopeResolution).getScope())
165166
) and
166-
child = SynthChild(SelfKind()) and
167+
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
167168
i = 0
168169
}
169170

ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ private module Cached {
133133
not scopeDefinesParameterVariable(scope, name, _) and
134134
not inherits(scope, name, _)
135135
} or
136+
TSelfVariable(SelfBase::Range scope) or
136137
TLocalVariableSynth(AstNode n, int i) { any(Synthesis s).localVariable(n, i) }
137138

138139
// Db types that can be vcalls
@@ -307,7 +308,8 @@ private module Cached {
307308
access(this, _) or
308309
this instanceof Ruby::GlobalVariable or
309310
this instanceof Ruby::InstanceVariable or
310-
this instanceof Ruby::ClassVariable
311+
this instanceof Ruby::ClassVariable or
312+
this instanceof Ruby::Self
311313
}
312314
}
313315

@@ -374,9 +376,10 @@ abstract class VariableImpl extends TVariable {
374376
abstract Location getLocationImpl();
375377
}
376378

377-
class TVariableReal = TGlobalVariable or TClassVariable or TInstanceVariable or TLocalVariableReal;
379+
class TVariableReal =
380+
TGlobalVariable or TClassVariable or TInstanceVariable or TLocalVariableReal or TSelfVariable;
378381

379-
class TLocalVariable = TLocalVariableReal or TLocalVariableSynth;
382+
class TLocalVariable = TLocalVariableReal or TLocalVariableSynth or TSelfVariable;
380383

381384
/**
382385
* This class only exists to avoid negative recursion warnings. Ideally,
@@ -475,6 +478,18 @@ class ClassVariableImpl extends VariableReal, TClassVariable {
475478
final override Scope::Range getDeclaringScopeImpl() { result = scope }
476479
}
477480

481+
class SelfVariableImpl extends VariableReal, TSelfVariable {
482+
private SelfBase::Range scope;
483+
484+
SelfVariableImpl() { this = TSelfVariable(scope) }
485+
486+
final override string getNameImpl() { result = "self" }
487+
488+
final override Location getLocationImpl() { result = scope.getLocation() }
489+
490+
final override Scope::Range getDeclaringScopeImpl() { result = scope }
491+
}
492+
478493
abstract class VariableAccessImpl extends Expr, TVariableAccess {
479494
abstract VariableImpl getVariableImpl();
480495
}
@@ -602,3 +617,26 @@ private class ClassVariableAccessSynth extends ClassVariableAccessRealImpl,
602617

603618
final override string toString() { result = v.getName() }
604619
}
620+
621+
abstract class SelfVariableAccessImpl extends LocalVariableAccessImpl, TSelfVariableAccess { }
622+
623+
private class SelfVariableAccessReal extends SelfVariableAccessImpl, TSelfReal {
624+
private Ruby::Self self;
625+
private SelfVariable var;
626+
627+
SelfVariableAccessReal() { this = TSelfReal(self) and var = TSelfVariable(scopeOf(self)) }
628+
629+
final override SelfVariable getVariableImpl() { result = var }
630+
631+
final override string toString() { result = var.toString() }
632+
}
633+
634+
private class SelfVariableAccessSynth extends SelfVariableAccessImpl, TSelfSynth {
635+
private SelfVariable v;
636+
637+
SelfVariableAccessSynth() { this = TSelfSynth(_, _, v) }
638+
639+
final override LocalVariable getVariableImpl() { result = v }
640+
641+
final override string toString() { result = v.getName() }
642+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class ExitNode extends CfgNode, TExitNode {
5959
/**
6060
* A node for an AST node.
6161
*
62-
* Each AST node maps to zero or more `AstCfgNode`s: zero when the node in unreachable
62+
* Each AST node maps to zero or more `AstCfgNode`s: zero when the node is unreachable
6363
* (dead) code or not important for control flow, and multiple when there are different
6464
* splits for the AST node.
6565
*/

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

Lines changed: 17 additions & 10 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

@@ -956,8 +965,6 @@ module Trees {
956965
final override ControlFlowTree getChildElement(int i) { result = this.getValue() and i = 0 }
957966
}
958967

959-
private class SelfTree extends LeafTree, Self { }
960-
961968
private class SimpleParameterTree extends NonDefaultValueParameterTree, SimpleParameter { }
962969

963970
// Corner case: For duplicated '_' parameters, only the first occurence has a defining

0 commit comments

Comments
 (0)