Skip to content

Commit 44b734e

Browse files
authored
Merge pull request github#13955 from hvitved/ruby/type-tracking-capture-insensitive
Ruby: Make type tracking flow-insensitive for captured variables
2 parents 6a3b9e1 + c084a9b commit 44b734e

File tree

4 files changed

+81
-29
lines changed

4 files changed

+81
-29
lines changed

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

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -137,22 +137,6 @@ module LocalFlow {
137137
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNodeImpl).getMethod())
138138
}
139139

140-
/**
141-
* Holds if `nodeFrom -> nodeTo` is a step from a parameter to a capture entry node for
142-
* that parameter.
143-
*
144-
* This is intended to recover from flow not currently recognised by ordinary capture flow.
145-
*/
146-
predicate localFlowSsaParamCaptureInput(ParameterNodeImpl nodeFrom, Node nodeTo) {
147-
exists(Ssa::CapturedEntryDefinition def |
148-
nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def
149-
|
150-
nodeFrom.getParameter().(NamedParameter).getVariable() = def.getSourceVariable()
151-
or
152-
nodeFrom.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
153-
)
154-
}
155-
156140
/**
157141
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
158142
* involving SSA definition `def`.
@@ -285,6 +269,53 @@ predicate isNonConstantExpr(CfgNodes::ExprCfgNode n) {
285269
not n.getExpr() instanceof ConstantAccess
286270
}
287271

272+
/** Provides logic related to captured variables. */
273+
module VariableCapture {
274+
class CapturedVariable extends LocalVariable {
275+
CapturedVariable() { this.isCaptured() }
276+
277+
CfgScope getCfgScope() {
278+
exists(Scope scope | scope = this.getDeclaringScope() |
279+
result = scope
280+
or
281+
result = scope.(ModuleBase).getCfgScope()
282+
)
283+
}
284+
}
285+
286+
class CapturedSsaDefinitionExt extends SsaImpl::DefinitionExt {
287+
CapturedSsaDefinitionExt() { this.getSourceVariable() instanceof CapturedVariable }
288+
}
289+
290+
/**
291+
* Holds if there is control-flow insensitive data-flow from `node1` to `node2`
292+
* involving a captured variable. Only used in type tracking.
293+
*/
294+
predicate flowInsensitiveStep(Node node1, Node node2) {
295+
exists(CapturedSsaDefinitionExt def, CapturedVariable v |
296+
// From an assignment or implicit initialization of a captured variable to its flow-insensitive node
297+
def = node1.(SsaDefinitionExtNode).getDefinitionExt() and
298+
def.getSourceVariable() = v and
299+
(
300+
def instanceof Ssa::WriteDefinition
301+
or
302+
def instanceof Ssa::SelfDefinition
303+
) and
304+
node2.(CapturedVariableNode).getVariable() = v
305+
or
306+
// From a captured variable node to its flow-sensitive capture nodes
307+
node1.(CapturedVariableNode).getVariable() = v and
308+
def = node2.(SsaDefinitionExtNode).getDefinitionExt() and
309+
def.getSourceVariable() = v and
310+
(
311+
def instanceof Ssa::CapturedCallDefinition
312+
or
313+
def instanceof Ssa::CapturedEntryDefinition
314+
)
315+
)
316+
}
317+
}
318+
288319
/** A collection of cached types and predicates to be evaluated in the same stage. */
289320
cached
290321
private module Cached {
@@ -296,6 +327,7 @@ private module Cached {
296327
TExprNode(CfgNodes::ExprCfgNode n) { TaintTrackingPrivate::forceCachingInSameStage() } or
297328
TReturningNode(CfgNodes::ReturningCfgNode n) or
298329
TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) or
330+
TCapturedVariableNode(VariableCapture::CapturedVariable v) or
299331
TNormalParameterNode(Parameter p) {
300332
p instanceof SimpleParameter or
301333
p instanceof OptionalParameter or
@@ -389,6 +421,8 @@ private module Cached {
389421
LocalFlow::localFlowSsaInputFromRead(exprFrom, _, nodeTo) and
390422
exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr()]
391423
)
424+
or
425+
VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo)
392426
}
393427

394428
private predicate entrySsaDefinition(SsaDefinitionExtNode n) {
@@ -550,7 +584,7 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
550584
}
551585

552586
/** An SSA definition for a `self` variable. */
553-
class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionExtNode {
587+
class SsaSelfDefinitionNode extends SsaDefinitionExtNode {
554588
private SelfVariable self;
555589

556590
SsaSelfDefinitionNode() { self = def.getSourceVariable() }
@@ -559,6 +593,22 @@ class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionExtNode {
559593
Scope getSelfScope() { result = self.getDeclaringScope() }
560594
}
561595

596+
/** A data flow node representing a captured variable. Only used in type tracking. */
597+
class CapturedVariableNode extends NodeImpl, TCapturedVariableNode {
598+
private VariableCapture::CapturedVariable variable;
599+
600+
CapturedVariableNode() { this = TCapturedVariableNode(variable) }
601+
602+
/** Gets the captured variable represented by this node. */
603+
VariableCapture::CapturedVariable getVariable() { result = variable }
604+
605+
override CfgScope getCfgScope() { result = variable.getCfgScope() }
606+
607+
override Location getLocationImpl() { result = variable.getLocation() }
608+
609+
override string toStringImpl() { result = "captured " + variable.getName() }
610+
}
611+
562612
/**
563613
* A value returning statement, viewed as a node in a data flow graph.
564614
*
@@ -1163,13 +1213,7 @@ private module OutNodes {
11631213

11641214
import OutNodes
11651215

1166-
predicate jumpStep(Node pred, Node succ) {
1167-
SsaImpl::captureFlowIn(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
1168-
succ.(SsaDefinitionExtNode).getDefinitionExt())
1169-
or
1170-
SsaImpl::captureFlowOut(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
1171-
succ.(SsaDefinitionExtNode).getDefinitionExt())
1172-
or
1216+
predicate jumpStepTypeTracker(Node pred, Node succ) {
11731217
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
11741218
or
11751219
FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(),
@@ -1178,6 +1222,16 @@ predicate jumpStep(Node pred, Node succ) {
11781222
any(AdditionalJumpStep s).step(pred, succ)
11791223
}
11801224

1225+
predicate jumpStep(Node pred, Node succ) {
1226+
jumpStepTypeTracker(pred, succ)
1227+
or
1228+
SsaImpl::captureFlowIn(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
1229+
succ.(SsaDefinitionExtNode).getDefinitionExt())
1230+
or
1231+
SsaImpl::captureFlowOut(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
1232+
succ.(SsaDefinitionExtNode).getDefinitionExt())
1233+
}
1234+
11811235
private ContentSet getKeywordContent(string name) {
11821236
exists(ConstantValue::ConstantSymbolValue key |
11831237
result.isSingleton(TKnownElementContent(key)) and

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,9 @@ private module Cached {
363363
source = sink and
364364
source instanceof LocalSourceNode
365365
or
366-
exists(Node mid | hasLocalSource(mid, source) |
366+
exists(Node mid |
367+
hasLocalSource(mid, source) and
367368
localFlowStepTypeTracker(mid, sink)
368-
or
369-
LocalFlow::localFlowSsaParamCaptureInput(mid, sink)
370369
)
371370
}
372371

ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ predicate simpleLocalFlowStep = DataFlowPrivate::localFlowStepTypeTracker/2;
7474
/**
7575
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
7676
*/
77-
predicate jumpStep = DataFlowPrivate::jumpStep/2;
77+
predicate jumpStep = DataFlowPrivate::jumpStepTypeTracker/2;
7878

7979
/** Holds if there is direct flow from `param` to a return. */
8080
pragma[nomagic]

ruby/ql/test/library-tests/dataflow/array-flow/type-tracking-array-flow.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ testFailures
1515
| array_flow.rb:376:10:376:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
1616
| array_flow.rb:377:10:377:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
1717
| array_flow.rb:378:10:378:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
18-
| array_flow.rb:407:12:407:30 | # $ hasValueFlow=45 | Missing result:hasValueFlow=45 |
1918
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.3 |
2019
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.4 |
2120
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.5 |

0 commit comments

Comments
 (0)