Skip to content

Commit 620e8dc

Browse files
authored
Merge pull request #14787 from hvitved/ruby/prune-dataflow-nodes
Ruby: Prune irrelevant data flow nodes and edges
2 parents 5b4a888 + b2f1022 commit 620e8dc

File tree

3 files changed

+119
-116
lines changed

3 files changed

+119
-116
lines changed

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

Lines changed: 117 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -398,25 +398,29 @@ module VariableCapture {
398398
CapturedSsaDefinitionExt() { this.getSourceVariable() instanceof CapturedVariable }
399399
}
400400

401-
/**
402-
* Holds if there is control-flow insensitive data-flow from `node1` to `node2`
403-
* involving a captured variable. Only used in type tracking.
404-
*/
405-
predicate flowInsensitiveStep(Node node1, Node node2) {
406-
exists(CapturedSsaDefinitionExt def, CapturedVariable v |
407-
// From an assignment or implicit initialization of a captured variable to its flow-insensitive node
408-
def = node1.(SsaDefinitionExtNode).getDefinitionExt() and
401+
// From an assignment or implicit initialization of a captured variable to its flow-insensitive node
402+
private predicate flowInsensitiveWriteStep(
403+
SsaDefinitionExtNode node1, CapturedVariableNode node2, CapturedVariable v
404+
) {
405+
exists(CapturedSsaDefinitionExt def |
406+
def = node1.getDefinitionExt() and
409407
def.getSourceVariable() = v and
410408
(
411409
def instanceof Ssa::WriteDefinition
412410
or
413411
def instanceof Ssa::SelfDefinition
414412
) and
415-
node2.(CapturedVariableNode).getVariable() = v
416-
or
417-
// From a captured variable node to its flow-sensitive capture nodes
418-
node1.(CapturedVariableNode).getVariable() = v and
419-
def = node2.(SsaDefinitionExtNode).getDefinitionExt() and
413+
node2.getVariable() = v
414+
)
415+
}
416+
417+
// From a captured variable node to its flow-sensitive capture nodes
418+
private predicate flowInsensitiveReadStep(
419+
CapturedVariableNode node1, SsaDefinitionExtNode node2, CapturedVariable v
420+
) {
421+
exists(CapturedSsaDefinitionExt def |
422+
node1.getVariable() = v and
423+
def = node2.getDefinitionExt() and
420424
def.getSourceVariable() = v and
421425
(
422426
def instanceof Ssa::CapturedCallDefinition
@@ -425,6 +429,20 @@ module VariableCapture {
425429
)
426430
)
427431
}
432+
433+
/**
434+
* Holds if there is control-flow insensitive data-flow from `node1` to `node2`
435+
* involving a captured variable. Only used in type tracking.
436+
*/
437+
predicate flowInsensitiveStep(Node node1, Node node2) {
438+
exists(CapturedVariable v |
439+
flowInsensitiveWriteStep(node1, node2, v) and
440+
flowInsensitiveReadStep(_, _, v)
441+
or
442+
flowInsensitiveReadStep(node1, node2, v) and
443+
flowInsensitiveWriteStep(_, _, v)
444+
)
445+
}
428446
}
429447

430448
private predicate splatParameterAt(Callable c, int pos) {
@@ -443,7 +461,7 @@ private module Cached {
443461
cached
444462
newtype TNode =
445463
TExprNode(CfgNodes::ExprCfgNode n) or
446-
TReturningNode(CfgNodes::ReturningCfgNode n) or
464+
TReturningNode(CfgNodes::ReturningCfgNode n) { exists(n.getReturnedValueNode()) } or
447465
TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) or
448466
TCapturedVariableNode(VariableCapture::CapturedVariable v) or
449467
TNormalParameterNode(Parameter p) {
@@ -478,11 +496,11 @@ private module Cached {
478496
} or
479497
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
480498
TSynthHashSplatArgumentNode(CfgNodes::ExprNodes::CallCfgNode c) {
481-
exists(Argument arg | arg.isArgumentOf(c, any(ArgumentPosition pos | pos.isKeyword(_))))
482-
or
483-
c.getAnArgument() instanceof CfgNodes::ExprNodes::PairCfgNode
499+
ArgumentNodes::synthHashSplatStore(c, _, _)
500+
} or
501+
TSynthSplatArgumentNode(CfgNodes::ExprNodes::CallCfgNode c) {
502+
ArgumentNodes::synthSplatStore(c, _, _)
484503
} or
485-
TSynthSplatArgumentNode(CfgNodes::ExprNodes::CallCfgNode c) or
486504
TSynthSplatArgumentShiftNode(CfgNodes::ExprNodes::CallCfgNode c, int splatPos, int n) {
487505
// we use -1 to represent data at an unknown index
488506
n in [-1 .. 10] and
@@ -652,7 +670,7 @@ private module Cached {
652670
)
653671
} or
654672
TSplatContent(int i, Boolean shifted) { i in [0 .. 10] } or
655-
THashSplatContent(ConstantValue cv) { not cv.isInt(_) } or
673+
THashSplatContent(ConstantValue::ConstantSymbolValue cv) or
656674
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
657675
// Only used by type-tracking
658676
TAttributeName(string name) { name = any(SetterMethodCall c).getTargetName() }
@@ -1269,6 +1287,51 @@ module ArgumentNodes {
12691287
final override Location getLocationImpl() { result = call_.getLocation() }
12701288
}
12711289

1290+
/**
1291+
* Holds if a store-step should be added from keyword argument `arg`, belonging to
1292+
* `call`, into a synthetic hash splat argument.
1293+
*/
1294+
predicate synthHashSplatStore(
1295+
CfgNodes::ExprNodes::CallCfgNode call, CfgNodes::ExprCfgNode arg, ContentSet c
1296+
) {
1297+
exists(ConstantValue cv |
1298+
// symbol key
1299+
exists(ArgumentPosition keywordPos, string name |
1300+
arg.(Argument).isArgumentOf(call, keywordPos) and
1301+
keywordPos.isKeyword(name) and
1302+
cv.isSymbol(name)
1303+
)
1304+
or
1305+
// non-symbol key
1306+
exists(CfgNodes::ExprNodes::PairCfgNode pair, CfgNodes::ExprCfgNode key |
1307+
arg = pair.getValue() and
1308+
pair = call.getAnArgument() and
1309+
key = pair.getKey() and
1310+
cv = key.getConstantValue() and
1311+
not cv.isSymbol(_)
1312+
)
1313+
|
1314+
if call instanceof CfgNodes::ExprNodes::HashLiteralCfgNode
1315+
then
1316+
/*
1317+
* Needed for cases like
1318+
*
1319+
* ```rb
1320+
* hash = { a: taint, b: safe }
1321+
*
1322+
* def foo(a:, b:)
1323+
* sink(a)
1324+
* end
1325+
*
1326+
* foo(**hash)
1327+
* ```
1328+
*/
1329+
1330+
c.isSingleton(Content::getElementContent(cv))
1331+
else c.isSingleton(THashSplatContent(cv))
1332+
)
1333+
}
1334+
12721335
/**
12731336
* A data-flow node that represents all keyword arguments wrapped in a hash.
12741337
*
@@ -1285,44 +1348,7 @@ module ArgumentNodes {
12851348
* Holds if a store-step should be added from argument `arg` into this synthetic
12861349
* hash-splat argument.
12871350
*/
1288-
predicate storeFrom(Node arg, ContentSet c) {
1289-
exists(ConstantValue cv |
1290-
if call_ instanceof CfgNodes::ExprNodes::HashLiteralCfgNode
1291-
then
1292-
/*
1293-
* Needed for cases like
1294-
*
1295-
* ```rb
1296-
* hash = { a: taint, b: safe }
1297-
*
1298-
* def foo(a:, b:)
1299-
* sink(a)
1300-
* end
1301-
*
1302-
* foo(**hash)
1303-
* ```
1304-
*/
1305-
1306-
c.isSingleton(TKnownElementContent(cv))
1307-
else c.isSingleton(THashSplatContent(cv))
1308-
|
1309-
// symbol key
1310-
exists(ArgumentPosition keywordPos, string name |
1311-
arg.asExpr().(Argument).isArgumentOf(call_, keywordPos) and
1312-
keywordPos.isKeyword(name) and
1313-
cv.isSymbol(name)
1314-
)
1315-
or
1316-
// non-symbol key
1317-
exists(CfgNodes::ExprNodes::PairCfgNode pair, CfgNodes::ExprCfgNode key |
1318-
arg.asExpr() = pair.getValue() and
1319-
pair = call_.getAnArgument() and
1320-
key = pair.getKey() and
1321-
cv = key.getConstantValue() and
1322-
not cv.isSymbol(_)
1323-
)
1324-
)
1325-
}
1351+
predicate storeFrom(Node arg, ContentSet c) { synthHashSplatStore(call_, arg.asExpr(), c) }
13261352

13271353
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
13281354
call = call_ and
@@ -1332,6 +1358,39 @@ module ArgumentNodes {
13321358
override string toStringImpl() { result = "synthetic hash-splat argument" }
13331359
}
13341360

1361+
/**
1362+
* Holds if a store-step should be added from positional argument `arg`, belonging to
1363+
* `call`, into a synthetic splat argument.
1364+
*/
1365+
predicate synthSplatStore(CfgNodes::ExprNodes::CallCfgNode call, Argument arg, ContentSet c) {
1366+
exists(int n |
1367+
exists(ArgumentPosition pos |
1368+
arg.isArgumentOf(call, pos) and
1369+
pos.isPositional(n) and
1370+
not exists(int i | splatArgumentAt(call, i) and i < n)
1371+
)
1372+
|
1373+
if call instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode
1374+
then
1375+
/*
1376+
* Needed for cases like
1377+
*
1378+
* ```rb
1379+
* arr = [taint, safe]
1380+
*
1381+
* def foo(a, b)
1382+
* sink(a)
1383+
* end
1384+
*
1385+
* foo(*arr)
1386+
* ```
1387+
*/
1388+
1389+
c = getArrayContent(n)
1390+
else c = getSplatContent(n, false)
1391+
)
1392+
}
1393+
13351394
/**
13361395
* A data-flow node that represents all positional arguments wrapped in an array.
13371396
*
@@ -1346,31 +1405,7 @@ module ArgumentNodes {
13461405
* Holds if a store-step should be added from argument `arg` into this synthetic
13471406
* splat argument.
13481407
*/
1349-
predicate storeFrom(Node arg, ContentSet c) {
1350-
exists(ArgumentPosition pos, int n |
1351-
arg.asExpr().(Argument).isArgumentOf(call_, pos) and
1352-
pos.isPositional(n) and
1353-
not exists(int i | splatArgumentAt(call_, i) and i < n) and
1354-
if call_ instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode
1355-
then
1356-
/*
1357-
* Needed for cases like
1358-
*
1359-
* ```rb
1360-
* arr = [taint, safe]
1361-
*
1362-
* def foo(a, b)
1363-
* sink(a)
1364-
* end
1365-
*
1366-
* foo(*arr)
1367-
* ```
1368-
*/
1369-
1370-
c = getArrayContent(n)
1371-
else c = getSplatContent(n, false)
1372-
)
1373-
}
1408+
predicate storeFrom(Node arg, ContentSet c) { synthSplatStore(call_, arg.asExpr(), c) }
13741409

13751410
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
13761411
call = call_ and

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -606,12 +606,12 @@ module Content {
606606
* we have an implicit hash-splat argument containing `{:a => 1, :b => 2, :c => 3}`.
607607
*/
608608
class HashSplatContent extends ElementContent, THashSplatContent {
609-
private ConstantValue cv;
609+
private ConstantValue::ConstantSymbolValue cv;
610610

611611
HashSplatContent() { this = THashSplatContent(cv) }
612612

613613
/** Gets the hash key. */
614-
ConstantValue getKey() { result = cv }
614+
ConstantValue::ConstantSymbolValue getKey() { result = cv }
615615

616616
override string toString() { result = "hash-splat position " + cv }
617617
}

0 commit comments

Comments
 (0)