Skip to content

Commit 5adf10f

Browse files
committed
Data flow: Add return context to pruning stages 2-4
1 parent ca17c5b commit 5adf10f

File tree

2 files changed

+102
-57
lines changed

2 files changed

+102
-57
lines changed

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

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,10 +1489,6 @@ private module MkStage<StageSig PrevStage> {
14891489
parameterFlowThroughAllowed(p, ret.getKind())
14901490
}
14911491

1492-
predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) {
1493-
returnFlowsThrough(ret, _, _, _, _, _, config)
1494-
}
1495-
14961492
pragma[nomagic]
14971493
private predicate flowThroughIntoCall(
14981494
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
@@ -1507,52 +1503,58 @@ private module MkStage<StageSig PrevStage> {
15071503
* Holds if `node` with access path `ap` is part of a path from a source to a
15081504
* sink in the configuration `config`.
15091505
*
1510-
* The Boolean `toReturn` records whether the node must be returned from the
1511-
* enclosing callable in order to reach a sink, and if so, `returnAp` records
1512-
* the access path of the returned value.
1506+
* The parameter `returnCtx` records whether (and how) the node must be returned
1507+
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
1508+
* records the access path of the returned value.
15131509
*/
15141510
pragma[nomagic]
15151511
additional predicate revFlow(
1516-
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
1512+
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
1513+
Configuration config
15171514
) {
1518-
revFlow0(node, state, toReturn, returnAp, ap, config) and
1515+
revFlow0(node, state, returnCtx, returnAp, ap, config) and
15191516
fwdFlow(node, state, _, _, _, ap, config)
15201517
}
15211518

15221519
pragma[nomagic]
15231520
private predicate revFlow0(
1524-
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
1521+
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
1522+
Configuration config
15251523
) {
15261524
fwdFlow(node, state, _, _, _, ap, config) and
15271525
sinkNode(node, state, config) and
1528-
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
1526+
(
1527+
if hasSinkCallCtx(config)
1528+
then returnCtx = TReturnCtxNoFlowThrough()
1529+
else returnCtx = TReturnCtxNone()
1530+
) and
15291531
returnAp = apNone() and
15301532
ap instanceof ApNil
15311533
or
15321534
exists(NodeEx mid, FlowState state0 |
15331535
localStep(node, state, mid, state0, true, _, config, _) and
1534-
revFlow(mid, state0, toReturn, returnAp, ap, config)
1536+
revFlow(mid, state0, returnCtx, returnAp, ap, config)
15351537
)
15361538
or
15371539
exists(NodeEx mid, FlowState state0, ApNil nil |
15381540
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
15391541
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
1540-
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
1542+
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
15411543
ap instanceof ApNil
15421544
)
15431545
or
15441546
exists(NodeEx mid |
15451547
jumpStep(node, mid, config) and
15461548
revFlow(mid, state, _, _, ap, config) and
1547-
toReturn = false and
1549+
returnCtx = TReturnCtxNone() and
15481550
returnAp = apNone()
15491551
)
15501552
or
15511553
exists(NodeEx mid, ApNil nil |
15521554
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
15531555
additionalJumpStep(node, mid, config) and
15541556
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
1555-
toReturn = false and
1557+
returnCtx = TReturnCtxNone() and
15561558
returnAp = apNone() and
15571559
ap instanceof ApNil
15581560
)
@@ -1562,47 +1564,50 @@ private module MkStage<StageSig PrevStage> {
15621564
additionalJumpStateStep(node, state, mid, state0, config) and
15631565
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
15641566
pragma[only_bind_into](config)) and
1565-
toReturn = false and
1567+
returnCtx = TReturnCtxNone() and
15661568
returnAp = apNone() and
15671569
ap instanceof ApNil
15681570
)
15691571
or
15701572
// store
15711573
exists(Ap ap0, Content c |
1572-
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
1574+
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
15731575
revFlowConsCand(ap0, c, ap, config)
15741576
)
15751577
or
15761578
// read
15771579
exists(NodeEx mid, Ap ap0 |
1578-
revFlow(mid, state, toReturn, returnAp, ap0, config) and
1580+
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
15791581
readStepFwd(node, ap, _, mid, ap0, config)
15801582
)
15811583
or
15821584
// flow into a callable
15831585
revFlowInNotToReturn(node, state, returnAp, ap, config) and
1584-
toReturn = false
1586+
returnCtx = TReturnCtxNone()
15851587
or
15861588
// flow through a callable
1587-
exists(DataFlowCall call, Ap returnAp0 |
1588-
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
1589-
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
1589+
exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 |
1590+
revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and
1591+
revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config)
15901592
)
15911593
or
15921594
// flow out of a callable
15931595
revFlowOut(_, node, state, _, _, ap, config) and
1594-
toReturn = true and
15951596
if returnFlowsThrough(node, state, _, _, _, ap, config)
1596-
then returnAp = apSome(ap)
1597-
else returnAp = apNone()
1597+
then (
1598+
returnCtx = TReturnCtxMaybeFlowThrough(node.(RetNodeEx).getReturnPosition()) and
1599+
returnAp = apSome(ap)
1600+
) else (
1601+
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
1602+
)
15981603
}
15991604

16001605
pragma[nomagic]
16011606
private predicate revFlowStore(
16021607
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
1603-
boolean toReturn, ApOption returnAp, Configuration config
1608+
ReturnCtx returnCtx, ApOption returnAp, Configuration config
16041609
) {
1605-
revFlow(mid, state, toReturn, returnAp, ap0, config) and
1610+
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
16061611
storeStepFwd(node, ap, tc, mid, ap0, config) and
16071612
tc.getContent() = c
16081613
}
@@ -1622,11 +1627,11 @@ private module MkStage<StageSig PrevStage> {
16221627

16231628
pragma[nomagic]
16241629
private predicate revFlowOut(
1625-
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
1626-
Configuration config
1630+
DataFlowCall call, RetNodeEx ret, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
1631+
Ap ap, Configuration config
16271632
) {
16281633
exists(NodeEx out, boolean allowsFieldFlow |
1629-
revFlow(out, state, toReturn, returnAp, ap, config) and
1634+
revFlow(out, state, returnCtx, returnAp, ap, config) and
16301635
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
16311636
if allowsFieldFlow = false then ap instanceof ApNil else any()
16321637
)
@@ -1637,20 +1642,22 @@ private module MkStage<StageSig PrevStage> {
16371642
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
16381643
) {
16391644
exists(ParamNodeEx p, boolean allowsFieldFlow |
1640-
revFlow(p, state, false, returnAp, ap, config) and
1645+
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
16411646
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
16421647
if allowsFieldFlow = false then ap instanceof ApNil else any()
16431648
)
16441649
}
16451650

16461651
pragma[nomagic]
16471652
private predicate revFlowInToReturn(
1648-
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
1653+
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp,
1654+
Ap ap, Configuration config
16491655
) {
16501656
exists(ParamNodeEx p, boolean allowsFieldFlow |
1651-
revFlow(p, state, true, apSome(returnAp), ap, config) and
1657+
revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), apSome(returnAp), ap, config) and
16521658
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
1653-
if allowsFieldFlow = false then ap instanceof ApNil else any()
1659+
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
1660+
parameterFlowThroughAllowed(p.asNode(), returnPos.getKind())
16541661
)
16551662
}
16561663

@@ -1661,11 +1668,13 @@ private module MkStage<StageSig PrevStage> {
16611668
*/
16621669
pragma[nomagic]
16631670
private predicate revFlowIsReturned(
1664-
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
1671+
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
1672+
Configuration config
16651673
) {
16661674
exists(RetNodeEx ret, FlowState state, CcCall ccc |
1667-
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
1675+
revFlowOut(call, ret, state, returnCtx, returnAp, ap, config) and
16681676
returnFlowsThrough(ret, state, ccc, _, _, ap, config) and
1677+
pos = ret.getReturnPosition() and
16691678
matchesCall(ccc, call)
16701679
)
16711680
}
@@ -1736,34 +1745,39 @@ private module MkStage<StageSig PrevStage> {
17361745
validAp(ap, config)
17371746
}
17381747

1739-
pragma[noinline]
1740-
private predicate parameterFlow(
1741-
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
1748+
pragma[nomagic]
1749+
private predicate parameterFlowsThroughRev(
1750+
ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config
17421751
) {
1743-
revFlow(p, _, true, apSome(ap0), ap, config) and
1744-
c = p.getEnclosingCallable()
1752+
revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and
1753+
parameterFlowThroughAllowed(p.asNode(), returnPos.getKind())
17451754
}
17461755

1756+
pragma[nomagic]
17471757
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
1748-
exists(DataFlowCallable c, RetNodeEx ret, FlowState state, Ap ap0, ParameterPosition pos |
1749-
parameterFlow(p, ap, ap0, c, config) and
1750-
c = ret.getEnclosingCallable() and
1751-
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
1752-
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
1753-
returnFlowsThrough(ret, state, _, p.asNode(), ap, ap0, config) and
1754-
p.getPosition() = pos and
1755-
parameterFlowThroughAllowed(p.asNode(), ret.getKind())
1758+
exists(RetNodeEx ret |
1759+
returnFlowsThrough(ret, _, _, p.asNode(), ap, _, config) and
1760+
parameterFlowsThroughRev(p, ap, ret.getReturnPosition(), config)
1761+
)
1762+
}
1763+
1764+
pragma[nomagic]
1765+
predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) {
1766+
exists(ParamNodeEx p, Ap ap |
1767+
returnFlowsThrough(ret, _, _, p.asNode(), ap, _, config) and
1768+
parameterFlowsThroughRev(p, ap, ret.getReturnPosition(), config)
17561769
)
17571770
}
17581771

17591772
pragma[nomagic]
17601773
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
17611774
exists(
1762-
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
1775+
ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state,
1776+
ReturnCtx returnCtx, ApOption returnAp, Ap ap
17631777
|
1764-
revFlow(arg, state, toReturn, returnAp, ap, config) and
1765-
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
1766-
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
1778+
revFlow(arg, state, returnCtx, returnAp, ap, config) and
1779+
revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and
1780+
revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config)
17671781
)
17681782
}
17691783

@@ -1786,8 +1800,8 @@ private module MkStage<StageSig PrevStage> {
17861800
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
17871801
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
17881802
tuples =
1789-
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
1790-
revFlow(n, state, b, retAp, ap, config)
1803+
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
1804+
revFlow(n, state, returnCtx, retAp, ap, config)
17911805
)
17921806
}
17931807
/* End: Stage logic. */
@@ -2250,7 +2264,7 @@ private predicate flowCandSummaryCtx(
22502264
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
22512265
) {
22522266
exists(AccessPathFront apf |
2253-
Stage3::revFlow(node, state, true, _, apf, config) and
2267+
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
22542268
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
22552269
config)
22562270
)
@@ -2530,7 +2544,7 @@ private predicate nodeMayUseSummary0(
25302544
) {
25312545
exists(AccessPathApprox apa0 |
25322546
Stage4::parameterMayFlowThrough(p, _, _) and
2533-
Stage4::revFlow(n, state, true, _, apa0, config) and
2547+
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
25342548
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
25352549
TAccessPathApproxSome(apa), apa0, config)
25362550
)

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,12 @@ private module Cached {
920920
TParamNodeNone() or
921921
TParamNodeSome(ParamNode p)
922922

923+
cached
924+
newtype TReturnCtx =
925+
TReturnCtxNone() or
926+
TReturnCtxNoFlowThrough() or
927+
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
928+
923929
cached
924930
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
925931

@@ -1322,6 +1328,31 @@ class ParamNodeOption extends TParamNodeOption {
13221328
}
13231329
}
13241330

1331+
/**
1332+
* A return context used to calculate flow summaries in reverse flow.
1333+
*
1334+
* The possible values are:
1335+
*
1336+
* - `TReturnCtxNone()`: no return flow.
1337+
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
1338+
* - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and
1339+
* flow through may be possible.
1340+
*/
1341+
class ReturnCtx extends TReturnCtx {
1342+
string toString() {
1343+
this = TReturnCtxNone() and
1344+
result = "(none)"
1345+
or
1346+
this = TReturnCtxNoFlowThrough() and
1347+
result = "(no flow through)"
1348+
or
1349+
exists(ReturnPosition pos |
1350+
this = TReturnCtxMaybeFlowThrough(pos) and
1351+
result = pos.toString()
1352+
)
1353+
}
1354+
}
1355+
13251356
/** A `Content` tagged with the type of a containing object. */
13261357
class TypedContent extends MkTypedContent {
13271358
private Content c;

0 commit comments

Comments
 (0)