Skip to content

Commit 70d2a0d

Browse files
committed
Data flow: Track parameter position instead of parameter in pruning stages 2-4
1 parent 4e4ee32 commit 70d2a0d

File tree

2 files changed

+104
-62
lines changed

2 files changed

+104
-62
lines changed

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

Lines changed: 95 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) {
613613
* explicitly allowed
614614
*/
615615
bindingset[p, kind]
616-
private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) {
616+
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
617617
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
618618
not kind.(ParamUpdateReturnKind).getPosition() = pos
619619
or
620-
allowParameterReturnInSelfCached(p)
620+
allowParameterReturnInSelfCached(p.asNode())
621+
)
622+
}
623+
624+
/**
625+
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
626+
* kind `kind` is allowed.
627+
*
628+
* We don't expect a parameter to return stored in itself, unless
629+
* explicitly allowed
630+
*/
631+
bindingset[c, pos, kind]
632+
private predicate parameterFlowThroughAllowed(
633+
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
634+
) {
635+
exists(ParamNodeEx p |
636+
p.isParameterOf(c, pos) and
637+
parameterFlowThroughAllowed(p, kind)
621638
)
622639
}
623640

@@ -1000,7 +1017,7 @@ private module Stage1 implements StageSig {
10001017
returnFlowCallableNodeCand(c, kind, config) and
10011018
p.getEnclosingCallable() = c and
10021019
exists(ap) and
1003-
parameterFlowThroughAllowed(p.asNode(), kind)
1020+
parameterFlowThroughAllowed(p, kind)
10041021
)
10051022
}
10061023

@@ -1102,6 +1119,7 @@ private predicate flowIntoCallNodeCand1(
11021119
* edge in the graph of paths between sources and sinks that ignores call
11031120
* contexts.
11041121
*/
1122+
pragma[nomagic]
11051123
private int branch(NodeEx n1, Configuration conf) {
11061124
result =
11071125
strictcount(NodeEx n |
@@ -1114,6 +1132,7 @@ private int branch(NodeEx n1, Configuration conf) {
11141132
* edge in the graph of paths between sources and sinks that ignores call
11151133
* contexts.
11161134
*/
1135+
pragma[nomagic]
11171136
private int join(NodeEx n2, Configuration conf) {
11181137
result =
11191138
strictcount(NodeEx n |
@@ -1152,10 +1171,10 @@ pragma[nomagic]
11521171
private predicate flowIntoCallNodeCand1(
11531172
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
11541173
) {
1155-
flowIntoCallNodeCand1(call, arg, p, config) and
1174+
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
11561175
exists(int b, int j |
1157-
b = branch(arg, config) and
1158-
j = join(p, config) and
1176+
b = branch(arg, pragma[only_bind_into](config)) and
1177+
j = join(p, pragma[only_bind_into](config)) and
11591178
if b.minimum(j) <= config.fieldFlowBranchLimit()
11601179
then allowsFieldFlow = true
11611180
else allowsFieldFlow = false
@@ -1266,15 +1285,16 @@ private module MkStage<StageSig PrevStage> {
12661285

12671286
pragma[nomagic]
12681287
private predicate flowThroughOutOfCall(
1269-
DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out,
1270-
boolean allowsFieldFlow, Configuration config
1288+
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
1289+
NodeEx out, boolean allowsFieldFlow, Configuration config
12711290
) {
12721291
exists(ReturnPosition pos |
12731292
flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and
12741293
kind = pos.getKind() and
12751294
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
12761295
PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and
1277-
matchesCall(ccc, call)
1296+
matchesCall(ccc, call) and
1297+
c = ret.getEnclosingCallable()
12781298
)
12791299
}
12801300

@@ -1284,12 +1304,12 @@ private module MkStage<StageSig PrevStage> {
12841304
*
12851305
* The call context `cc` records whether the node is reached through an
12861306
* argument in a call, and if so, `summaryCtx` and `argAp` record the
1287-
* corresponding parameter and access path of that argument, respectively.
1307+
* corresponding parameter position and access path of that argument, respectively.
12881308
*/
12891309
pragma[nomagic]
12901310
additional predicate fwdFlow(
1291-
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
1292-
Configuration config
1311+
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
1312+
Ap ap, Configuration config
12931313
) {
12941314
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
12951315
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
@@ -1298,13 +1318,13 @@ private module MkStage<StageSig PrevStage> {
12981318

12991319
pragma[nomagic]
13001320
private predicate fwdFlow0(
1301-
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
1302-
Configuration config
1321+
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
1322+
Ap ap, Configuration config
13031323
) {
13041324
sourceNode(node, state, config) and
13051325
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
13061326
argAp = apNone() and
1307-
summaryCtx = TParamNodeNone() and
1327+
summaryCtx = TParameterPositionNone() and
13081328
ap = getApNil(node)
13091329
or
13101330
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
@@ -1322,15 +1342,15 @@ private module MkStage<StageSig PrevStage> {
13221342
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
13231343
jumpStep(mid, node, config) and
13241344
cc = ccNone() and
1325-
summaryCtx = TParamNodeNone() and
1345+
summaryCtx = TParameterPositionNone() and
13261346
argAp = apNone()
13271347
)
13281348
or
13291349
exists(NodeEx mid, ApNil nil |
13301350
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
13311351
additionalJumpStep(mid, node, config) and
13321352
cc = ccNone() and
1333-
summaryCtx = TParamNodeNone() and
1353+
summaryCtx = TParameterPositionNone() and
13341354
argAp = apNone() and
13351355
ap = getApNil(node)
13361356
)
@@ -1339,7 +1359,7 @@ private module MkStage<StageSig PrevStage> {
13391359
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
13401360
additionalJumpStateStep(mid, state0, node, state, config) and
13411361
cc = ccNone() and
1342-
summaryCtx = TParamNodeNone() and
1362+
summaryCtx = TParameterPositionNone() and
13431363
argAp = apNone() and
13441364
ap = getApNil(node)
13451365
)
@@ -1362,17 +1382,18 @@ private module MkStage<StageSig PrevStage> {
13621382
apa = getApprox(ap) and
13631383
if PrevStage::parameterMayFlowThrough(node, apa, config)
13641384
then (
1365-
summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap)
1385+
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
1386+
argAp = apSome(ap)
13661387
) else (
1367-
summaryCtx = TParamNodeNone() and argAp = apNone()
1388+
summaryCtx = TParameterPositionNone() and argAp = apNone()
13681389
)
13691390
)
13701391
or
13711392
// flow out of a callable
13721393
fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config)
13731394
or
13741395
// flow through a callable
1375-
exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 |
1396+
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
13761397
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
13771398
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
13781399
)
@@ -1381,7 +1402,7 @@ private module MkStage<StageSig PrevStage> {
13811402
pragma[nomagic]
13821403
private predicate fwdFlowStore(
13831404
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
1384-
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
1405+
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
13851406
) {
13861407
exists(DataFlowType contentType |
13871408
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
@@ -1406,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
14061427
pragma[nomagic]
14071428
private predicate fwdFlowRead(
14081429
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
1409-
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
1430+
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
14101431
) {
14111432
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
14121433
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1416,7 +1437,7 @@ private module MkStage<StageSig PrevStage> {
14161437
pragma[nomagic]
14171438
private predicate fwdFlowIn(
14181439
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
1419-
ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config
1440+
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
14201441
) {
14211442
exists(ArgNodeEx arg, boolean allowsFieldFlow |
14221443
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
@@ -1428,8 +1449,8 @@ private module MkStage<StageSig PrevStage> {
14281449

14291450
pragma[nomagic]
14301451
private predicate fwdFlowOutNotFromArg(
1431-
NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
1432-
Configuration config
1452+
NodeEx out, FlowState state, Cc ccOut, ParameterPositionOption summaryCtx, ApOption argAp,
1453+
Ap ap, Configuration config
14331454
) {
14341455
exists(
14351456
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
@@ -1445,14 +1466,18 @@ private module MkStage<StageSig PrevStage> {
14451466

14461467
pragma[nomagic]
14471468
private predicate fwdFlowOutFromArg(
1448-
DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, Ap argAp, Ap ap,
1469+
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
14491470
Configuration config
14501471
) {
1451-
exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc |
1452-
fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and
1453-
flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and
1472+
exists(
1473+
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
1474+
|
1475+
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
1476+
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
1477+
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
1478+
config) and
14541479
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
1455-
parameterFlowThroughAllowed(summaryCtx, kind)
1480+
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
14561481
)
14571482
}
14581483

@@ -1462,13 +1487,13 @@ private module MkStage<StageSig PrevStage> {
14621487
*/
14631488
pragma[nomagic]
14641489
private predicate fwdFlowIsEntered(
1465-
DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap,
1466-
Configuration config
1490+
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
1491+
ParameterPosition pos, Ap ap, Configuration config
14671492
) {
14681493
exists(ParamNodeEx param |
14691494
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
14701495
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
1471-
p = param.asNode()
1496+
pos = param.getPosition()
14721497
)
14731498
}
14741499

@@ -1488,14 +1513,30 @@ private module MkStage<StageSig PrevStage> {
14881513
fwdFlowConsCand(ap1, c, ap2, config)
14891514
}
14901515

1516+
pragma[nomagic]
1517+
private predicate returnFlowsThrough0(
1518+
RetNodeEx ret, FlowState state, CcCall ccc, DataFlowCallable c, ParameterPosition ppos,
1519+
Ap argAp, Ap ap, Configuration config
1520+
) {
1521+
exists(boolean allowsFieldFlow |
1522+
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
1523+
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), _, _, allowsFieldFlow,
1524+
pragma[only_bind_into](config)) and
1525+
if allowsFieldFlow = false then ap instanceof ApNil else any()
1526+
)
1527+
}
1528+
14911529
pragma[nomagic]
14921530
private predicate returnFlowsThrough(
1493-
RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap,
1494-
Configuration config
1531+
RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
1532+
Ap ap, Configuration config
14951533
) {
1496-
fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and
1497-
pos = ret.getReturnPosition() and
1498-
parameterFlowThroughAllowed(p, pos.getKind())
1534+
exists(DataFlowCallable c, ParameterPosition ppos |
1535+
returnFlowsThrough0(ret, state, ccc, c, ppos, argAp, ap, config) and
1536+
p.isParameterOf(c, ppos) and
1537+
pos = ret.getReturnPosition() and
1538+
parameterFlowThroughAllowed(p, pos.getKind())
1539+
)
14991540
}
15001541

15011542
pragma[nomagic]
@@ -1506,7 +1547,7 @@ private module MkStage<StageSig PrevStage> {
15061547
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
15071548
pragma[only_bind_into](config)) and
15081549
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
1509-
returnFlowsThrough(_, _, _, _, p.asNode(), pragma[only_bind_into](argAp), _,
1550+
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
15101551
pragma[only_bind_into](config))
15111552
)
15121553
}
@@ -1671,7 +1712,7 @@ private module MkStage<StageSig PrevStage> {
16711712
revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), apSome(returnAp), ap, config) and
16721713
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
16731714
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
1674-
parameterFlowThroughAllowed(p.asNode(), returnPos.getKind())
1715+
parameterFlowThroughAllowed(p, returnPos.getKind())
16751716
)
16761717
}
16771718

@@ -1763,21 +1804,21 @@ private module MkStage<StageSig PrevStage> {
17631804
ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config
17641805
) {
17651806
revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and
1766-
parameterFlowThroughAllowed(p.asNode(), returnPos.getKind())
1807+
parameterFlowThroughAllowed(p, returnPos.getKind())
17671808
}
17681809

17691810
pragma[nomagic]
17701811
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
17711812
exists(RetNodeEx ret, ReturnPosition pos |
1772-
returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and
1813+
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
17731814
parameterFlowsThroughRev(p, ap, pos, config)
17741815
)
17751816
}
17761817

17771818
pragma[nomagic]
17781819
predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) {
17791820
exists(ParamNodeEx p, Ap ap |
1780-
returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and
1821+
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
17811822
parameterFlowsThroughRev(p, ap, pos, config)
17821823
)
17831824
}
@@ -1803,9 +1844,8 @@ private module MkStage<StageSig PrevStage> {
18031844
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
18041845
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
18051846
tuples =
1806-
count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
1807-
fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
1808-
)
1847+
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
1848+
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
18091849
or
18101850
fwd = false and
18111851
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -2555,12 +2595,13 @@ private Configuration unbindConf(Configuration conf) {
25552595

25562596
pragma[nomagic]
25572597
private predicate nodeMayUseSummary0(
2558-
NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
2598+
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
2599+
Configuration config
25592600
) {
25602601
exists(AccessPathApprox apa0 |
2561-
Stage4::parameterMayFlowThrough(p, _, _) and
2602+
c = n.getEnclosingCallable() and
25622603
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
2563-
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
2604+
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
25642605
TAccessPathApproxSome(apa), apa0, config)
25652606
)
25662607
}
@@ -2569,9 +2610,10 @@ pragma[nomagic]
25692610
private predicate nodeMayUseSummary(
25702611
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
25712612
) {
2572-
exists(ParamNodeEx p |
2613+
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
25732614
Stage4::parameterMayFlowThrough(p, apa, config) and
2574-
nodeMayUseSummary0(n, p, state, apa, config)
2615+
nodeMayUseSummary0(n, c, pos, state, apa, config) and
2616+
p.isParameterOf(c, pos)
25752617
)
25762618
}
25772619

@@ -3504,7 +3546,7 @@ private predicate paramFlowsThrough(
35043546
pathNode(mid, ret, state, cc, sc, ap, config, _) and
35053547
kind = ret.getKind() and
35063548
apa = ap.getApprox() and
3507-
parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind)
3549+
parameterFlowThroughAllowed(sc.getParamNode(), kind)
35083550
)
35093551
}
35103552

0 commit comments

Comments
 (0)