Skip to content

Commit 93121d9

Browse files
committed
Expand SSASourceVariable to include captured variables
1 parent 54edfc5 commit 93121d9

14 files changed

+104
-34
lines changed

Diff for: go/ql/lib/semmle/go/Scopes.qll

+3-2
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,9 @@ class LocalVariable extends DeclaredVariable {
255255
}
256256

257257
/** Holds if this variable is referenced inside a nested function. */
258-
predicate isCaptured() {
259-
this.getDeclaringFunction() != this.getAReference().getEnclosingFunction()
258+
predicate isCaptured(FuncDef capturingContainer) {
259+
capturingContainer = this.getAReference().getEnclosingFunction() and
260+
capturingContainer != this.getDeclaringFunction()
260261
}
261262
}
262263

Diff for: go/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll

+3-3
Original file line numberDiff line numberDiff line change
@@ -408,9 +408,9 @@ private predicate mkPureCall(DataFlow::CallNode ce, Function f, GVN callee, GvnL
408408
private predicate incompleteSsa(ValueEntity v) {
409409
not v instanceof Field and
410410
(
411-
not v instanceof SsaSourceVariable
411+
not v = any(SsaSourceVariable ssv).getVariable()
412412
or
413-
v.(SsaSourceVariable).mayHaveIndirectReferences()
413+
v = any(SsaSourceVariable ssv | ssv.mayHaveIndirectReferences()).getVariable()
414414
or
415415
exists(Type tp | tp = v.(DeclaredVariable).getType().getUnderlyingType() |
416416
not tp instanceof BasicType
@@ -560,7 +560,7 @@ GVN globalValueNumber(DataFlow::Node nd) {
560560
or
561561
exists(DataFlow::SsaNode ssa |
562562
nd = ssa.getAUse() and
563-
not incompleteSsa(ssa.getSourceVariable()) and
563+
not incompleteSsa(ssa.getSourceVariable().getVariable()) and
564564
result = globalValueNumber(ssa)
565565
)
566566
or

Diff for: go/ql/lib/semmle/go/dataflow/SSA.qll

+53-8
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,44 @@ private import SsaImpl
99
* A variable that can be SSA converted, that is, a local variable, but not a variable
1010
* declared in file scope.
1111
*/
12-
class SsaSourceVariable extends LocalVariable {
13-
SsaSourceVariable() { not this.getScope() instanceof FileScope }
12+
class SsaSourceVariable extends TSsaSourceVariable {
13+
/** Holds if this `SsaSourceVariable` is a captured variable. */
14+
predicate isCapturedVariable() { this instanceof MkCapturedVariableVariable }
15+
16+
/** Gets the variable corresponding to this `SsaSourceVariable`. */
17+
NonFileScopeLocalVariable getVariable() {
18+
this = MkNonCapturedVariableVariable(result) or
19+
this = MkCapturedVariableVariable(_, result)
20+
}
21+
22+
/** Gets the innermost function to which this `SsaSourceVariable` belongs. */
23+
FuncDef getRoot() {
24+
this instanceof MkNonCapturedVariableVariable and
25+
result = this.getVariable().getDeclaringFunction()
26+
or
27+
this = MkCapturedVariableVariable(result, _)
28+
}
29+
30+
/** Gets the type of this variable. */
31+
Type getType() { result = this.getVariable().getType() }
32+
33+
/** Gets a textual representation of this `SsaSourceVariable`. */
34+
string getName() {
35+
this instanceof MkNonCapturedVariableVariable and result = this.getVariable().getName()
36+
or
37+
exists(FuncDef root, NonFileScopeLocalVariable v | this = MkCapturedVariableVariable(root, v) |
38+
result = root.getName() + "(..)." + v.getName()
39+
or
40+
not exists(root.getName()) and
41+
result = "<anonymous>(..)." + v.getName()
42+
)
43+
}
44+
45+
/** Gets a textual representation of this `SsaSourceVariable`. */
46+
string toString() { result = this.getName() }
47+
48+
/** Gets the source location for this element. */
49+
Location getLocation() { result = this.getVariable().getLocation() }
1450

1551
/**
1652
* Holds if there may be indirect references of this variable that are not covered by `getAReference()`.
@@ -20,18 +56,18 @@ class SsaSourceVariable extends LocalVariable {
2056
*/
2157
predicate mayHaveIndirectReferences() {
2258
// variables that have their address taken
23-
exists(AddressExpr addr | addr.getOperand().stripParens() = this.getAReference())
59+
exists(AddressExpr addr | addr.getOperand().stripParens() = this.getVariable().getAReference())
2460
or
2561
exists(DataFlow::MethodReadNode mrn |
26-
mrn.getReceiver() = this.getARead() and
62+
mrn.getReceiver() = this.getVariable().getARead() and
2763
mrn.getMethod().getReceiverType() instanceof PointerType
2864
)
2965
or
3066
// variables where there is an unresolved reference with the same name in the same
3167
// scope or a nested scope, suggesting that name resolution information may be incomplete
3268
exists(FunctionScope scope, FuncDef inner |
33-
scope = this.getScope().(LocalScope).getEnclosingFunctionScope() and
34-
unresolvedReference(this.getName(), inner) and
69+
scope = this.getVariable().getScope().(LocalScope).getEnclosingFunctionScope() and
70+
unresolvedReference(this.getVariable().getName(), inner) and
3571
inner.getScope().getOuterScope*() = scope
3672
)
3773
}
@@ -178,6 +214,8 @@ class SsaDefinition instanceof ZZZDefinition {
178214
* An SSA definition that corresponds to an explicit assignment or other variable definition.
179215
*/
180216
class SsaExplicitDefinition extends SsaDefinition {
217+
SsaExplicitDefinition() { not this instanceof SsaImplicitDefinition }
218+
181219
/** Gets the instruction where the definition happens. */
182220
IR::Instruction getInstruction() {
183221
exists(BasicBlock bb, int i | this.definesAt(_, bb, i) | result = bb.getNode(i))
@@ -230,14 +268,21 @@ abstract class SsaImplicitDefinition extends SsaDefinition {
230268
*/
231269
class SsaVariableCapture extends SsaImplicitDefinition {
232270
SsaVariableCapture() {
233-
exists(BasicBlock bb, int i, SsaSourceVariable v | this.definesAt(v, bb, i) |
234-
mayCapture(bb, i, v)
271+
exists(BasicBlock bb, int j, SsaSourceVariable w |
272+
this.definesAt(w, bb, j) and
273+
mayCapture(bb, j, _, w)
235274
)
236275
}
237276

238277
override string getKind() { result = "capture" }
239278

240279
override string prettyPrintDef() { result = "capture variable " + this.getSourceVariable() }
280+
281+
override Location getLocation() {
282+
exists(ReachableBasicBlock bb, int i | this.definesAt(_, bb, i) |
283+
result = bb.getNode(i).getLocation()
284+
)
285+
}
241286
}
242287

243288
/**

Diff for: go/ql/lib/semmle/go/dataflow/SsaImpl.qll

+34-10
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,42 @@
77
import go
88
private import codeql.ssa.Ssa as SsaImplCommon
99

10+
class NonFileScopeLocalVariable extends LocalVariable {
11+
NonFileScopeLocalVariable() { not this.getScope() instanceof FileScope }
12+
}
13+
14+
cached
15+
newtype TSsaSourceVariable =
16+
MkNonCapturedVariableVariable(NonFileScopeLocalVariable v) or
17+
MkCapturedVariableVariable(FuncDef root, NonFileScopeLocalVariable v) { v.isCaptured(root) }
18+
1019
cached
1120
private module Internal {
1221
/** Holds if the `i`th node of `bb` defines `v`. */
1322
cached
1423
predicate defAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
15-
bb.getNode(i).(IR::Instruction).writes(v, _)
24+
exists(IR::Instruction inst | inst = bb.getNode(i).(IR::Instruction) |
25+
inst.writes(v.getVariable(), _) and
26+
inst.getRoot() = v.getRoot()
27+
)
1628
}
1729

1830
/** Holds if the `i`th node of `bb` reads `v`. */
1931
cached
2032
predicate useAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
21-
bb.getNode(i).(IR::Instruction).reads(v)
33+
exists(IR::Instruction inst | inst = bb.getNode(i).(IR::Instruction) |
34+
inst.reads(v.getVariable()) and
35+
inst.getRoot() = v.getRoot()
36+
)
2237
}
2338

2439
/**
2540
* Holds if `v` is a captured variable which is declared in `declFun` and read in `useFun`.
2641
*/
27-
private predicate readsCapturedVar(FuncDef useFun, SsaSourceVariable v, FuncDef declFun) {
42+
private predicate readsCapturedVar(FuncDef useFun, NonFileScopeLocalVariable v, FuncDef declFun) {
2843
declFun = v.getDeclaringFunction() and
2944
useFun = any(IR::Instruction u | u.reads(v)).getRoot() and
30-
v.isCaptured()
45+
v.isCaptured(_)
3146
}
3247

3348
/** Holds if the `i`th node of `bb` in function `f` is an entry node. */
@@ -50,8 +65,15 @@ private module Internal {
5065
* introduced depends on whether `v` is live at this point in the program.
5166
*/
5267
cached
53-
predicate mayCapture(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
54-
exists(FuncDef capturingContainer, FuncDef declContainer |
68+
predicate mayCapture(
69+
ReachableBasicBlock bb, int i, SsaSourceVariable capturedVar, SsaSourceVariable closureVar
70+
) {
71+
exists(LocalVariable v, FuncDef capturingContainer, FuncDef declContainer |
72+
v = capturedVar.getVariable() and
73+
v = closureVar.getVariable() and
74+
declContainer = capturedVar.getRoot() and
75+
capturingContainer = closureVar.getRoot()
76+
|
5577
// capture initial value of variable declared in enclosing scope
5678
readsCapturedVar(capturingContainer, v, declContainer) and
5779
capturingContainer != declContainer and
@@ -76,7 +98,7 @@ private module Internal {
7698
private predicate ref(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind tp) {
7799
useAt(bb, i, v) and tp = ReadRef()
78100
or
79-
(mayCapture(bb, i, v) or defAt(bb, i, v)) and
101+
(mayCapture(bb, i, v, _) or defAt(bb, i, v)) and
80102
tp = WriteRef()
81103
}
82104

@@ -114,7 +136,7 @@ private module Internal {
114136
/**
115137
* Holds if `v` is assigned outside its declaring function.
116138
*/
117-
private predicate assignedThroughClosure(SsaSourceVariable v) {
139+
private predicate assignedThroughClosure(NonFileScopeLocalVariable v) {
118140
any(IR::Instruction def | def.writes(v, _)).getRoot() != v.getDeclaringFunction()
119141
}
120142

@@ -407,6 +429,8 @@ private module Internal {
407429
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
408430
defAt(bb, i, v) and
409431
certain = true
432+
or
433+
mayCapture(bb, i, _, v) and certain = true
410434
}
411435

412436
/**
@@ -417,7 +441,7 @@ private module Internal {
417441
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
418442
useAt(bb, i, v) and certain = true
419443
or
420-
mayCapture(bb, i, v) and certain = false
444+
mayCapture(bb, i, v, _) and certain = true
421445
}
422446
}
423447

@@ -434,4 +458,4 @@ private module Internal {
434458

435459
import Internal
436460

437-
predicate captures = Internal::mayCapture/3;
461+
predicate captures = Internal::mayCapture/4;

Diff for: go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ module Public {
291291
/** Gets the unique definition of this SSA variable. */
292292
SsaDefinition getDefinition() { result = ssa }
293293

294-
override ControlFlow::Root getRoot() { result = ssa.getRoot() }
294+
override ControlFlow::Root getRoot() { result = ssa.getSourceVariable().getRoot() }
295295

296296
override Type getType() { result = ssa.getSourceVariable().getType() }
297297

Diff for: go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ private Field getASparselyUsedChannelTypedField() {
114114
*/
115115
predicate jumpStep(Node n1, Node n2) {
116116
exists(ValueEntity v |
117-
not v instanceof SsaSourceVariable and
117+
not v.getScope() instanceof FunctionScope and
118118
not v instanceof Field and
119119
(
120120
any(Write w).writes(v, n1)

Diff for: go/ql/lib/semmle/go/frameworks/Beego.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ module Beego {
5353
SsaWithFields v;
5454

5555
BeegoOutputInstance() {
56-
this = v.getBaseVariable().getSourceVariable() and
56+
this = v.getBaseVariable().getSourceVariable().getVariable() and
5757
v.getType().(PointerType).getBaseType().hasQualifiedName(contextPackagePath(), "BeegoOutput")
5858
}
5959

Diff for: go/ql/lib/semmle/go/frameworks/Fasthttp.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ module Fasthttp {
6161
SsaWithFields v;
6262

6363
ResponseWriter() {
64-
this = v.getBaseVariable().getSourceVariable() and
64+
this = v.getBaseVariable().getSourceVariable().getVariable() and
6565
(
6666
v.getType().hasQualifiedName(packagePath(), ["Response", "ResponseHeader"]) or
6767
v.getType().(PointerType).getBaseType().hasQualifiedName(packagePath(), "RequestCtx")

Diff for: go/ql/lib/semmle/go/frameworks/GinCors.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ module GinCors {
127127
SsaWithFields v;
128128

129129
GinConfig() {
130-
this = v.getBaseVariable().getSourceVariable() and
130+
this = v.getBaseVariable().getSourceVariable().getVariable() and
131131
v.getType().hasQualifiedName(packagePath(), "Config")
132132
}
133133

Diff for: go/ql/lib/semmle/go/frameworks/Macaron.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ private module Macaron {
99
SsaWithFields v;
1010

1111
Context() {
12-
this = v.getBaseVariable().getSourceVariable() and
12+
this = v.getBaseVariable().getSourceVariable().getVariable() and
1313
exists(Method m | m.hasQualifiedName("gopkg.in/macaron.v1", "Context", "Redirect") |
1414
v.getType().getMethod("Redirect") = m
1515
)

Diff for: go/ql/lib/semmle/go/frameworks/RsCors.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ module RsCors {
139139
SsaWithFields v;
140140

141141
RsOptions() {
142-
this = v.getBaseVariable().getSourceVariable() and
142+
this = v.getBaseVariable().getSourceVariable().getVariable() and
143143
v.getType().hasQualifiedName(packagePath(), "Options")
144144
}
145145

Diff for: go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module NetHttp {
1313
SsaWithFields v;
1414

1515
StdlibResponseWriter() {
16-
this = v.getBaseVariable().getSourceVariable() and
16+
this = v.getBaseVariable().getSourceVariable().getVariable() and
1717
exists(Type t | t.implements("net/http", "ResponseWriter") | v.getType() = t)
1818
}
1919

Diff for: go/ql/lib/semmle/go/security/IncorrectIntegerConversionLib.qll

+1-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ class TypeSwitchVarFlowStateTransformer extends DataFlow::SsaNode, FlowStateTran
319319

320320
TypeSwitchVarFlowStateTransformer() {
321321
exists(IR::TypeSwitchImplicitVariableInstruction insn, LocalVariable lv | insn.writes(lv, _) |
322-
this.getSourceVariable() = lv and
322+
this.getSourceVariable().getVariable() = lv and
323323
it = lv.getType().getUnderlyingType()
324324
)
325325
}

Diff for: go/ql/src/RedundantCode/DeadStoreOfLocal.ql

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ predicate isSimple(IR::Instruction nd) {
2929

3030
from IR::Instruction def, SsaSourceVariable target, IR::Instruction rhs
3131
where
32-
def.writes(target, rhs) and
32+
def.writes(target.getVariable(), rhs) and
3333
not exists(SsaExplicitDefinition ssa | ssa.getInstruction() = def) and
3434
// exclude assignments in dead code
3535
def.getBasicBlock() instanceof ReachableBasicBlock and
3636
// exclude assignments with default values or simple expressions
3737
not isSimple(rhs) and
3838
// exclude variables that are not used at all
39-
exists(target.getAReference()) and
39+
exists(target.getVariable().getAReference()) and
4040
// exclude variables with indirect references
4141
not target.mayHaveIndirectReferences()
4242
select def, "This definition of " + target + " is never used."

0 commit comments

Comments
 (0)