Skip to content

Commit a3a3b46

Browse files
committed
Data flow: Account for return nodes with multiple return kinds when restricting flow through
For example, flow out via parameters allows for return nodes with multiple return kinds: ```csharp void SetXOrY(C x, C y, bool b) { C c = x; if (b) c = y; c.Field = taint; // post-update node for `c` has two return kinds } ```
1 parent 5adf10f commit a3a3b46

File tree

1 file changed

+63
-51
lines changed

1 file changed

+63
-51
lines changed

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

Lines changed: 63 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,8 +1005,9 @@ private module Stage1 implements StageSig {
10051005
}
10061006

10071007
pragma[nomagic]
1008-
predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) {
1009-
throughFlowNodeCand(ret, config)
1008+
predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) {
1009+
throughFlowNodeCand(ret, config) and
1010+
pos = ret.getReturnPosition()
10101011
}
10111012

10121013
pragma[nomagic]
@@ -1065,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1(
10651066
*/
10661067
pragma[nomagic]
10671068
private predicate flowOutOfCallNodeCand1(
1068-
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
1069+
DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config
10691070
) {
1070-
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
1071+
viableReturnPosOutNodeCand1(call, pos, out, config) and
1072+
pos = ret.getReturnPosition() and
10711073
Stage1::revFlow(ret, config) and
10721074
not outBarrier(ret, config) and
10731075
not inBarrier(out, config)
@@ -1103,7 +1105,7 @@ private predicate flowIntoCallNodeCand1(
11031105
private int branch(NodeEx n1, Configuration conf) {
11041106
result =
11051107
strictcount(NodeEx n |
1106-
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
1108+
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
11071109
)
11081110
}
11091111

@@ -1115,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) {
11151117
private int join(NodeEx n2, Configuration conf) {
11161118
result =
11171119
strictcount(NodeEx n |
1118-
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
1120+
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
11191121
)
11201122
}
11211123

@@ -1128,12 +1130,13 @@ private int join(NodeEx n2, Configuration conf) {
11281130
*/
11291131
pragma[nomagic]
11301132
private predicate flowOutOfCallNodeCand1(
1131-
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
1133+
DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
1134+
Configuration config
11321135
) {
1133-
flowOutOfCallNodeCand1(call, ret, out, config) and
1136+
flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and
11341137
exists(int b, int j |
1135-
b = branch(ret, config) and
1136-
j = join(out, config) and
1138+
b = branch(ret, pragma[only_bind_into](config)) and
1139+
j = join(out, pragma[only_bind_into](config)) and
11371140
if b.minimum(j) <= config.fieldFlowBranchLimit()
11381141
then allowsFieldFlow = true
11391142
else allowsFieldFlow = false
@@ -1171,7 +1174,7 @@ private signature module StageSig {
11711174

11721175
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
11731176

1174-
predicate returnMayFlowThrough(RetNodeEx ret, Configuration config);
1177+
predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config);
11751178

11761179
predicate storeStepCand(
11771180
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1237,7 +1240,8 @@ private module MkStage<StageSig PrevStage> {
12371240
);
12381241

12391242
predicate flowOutOfCall(
1240-
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
1243+
DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
1244+
Configuration config
12411245
);
12421246

12431247
predicate flowIntoCall(
@@ -1262,13 +1266,16 @@ private module MkStage<StageSig PrevStage> {
12621266

12631267
pragma[nomagic]
12641268
private predicate flowThroughOutOfCall(
1265-
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
1266-
Configuration config
1269+
DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out,
1270+
boolean allowsFieldFlow, Configuration config
12671271
) {
1268-
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
1269-
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
1270-
PrevStage::returnMayFlowThrough(ret, pragma[only_bind_into](config)) and
1271-
matchesCall(ccc, call)
1272+
exists(ReturnPosition pos |
1273+
flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and
1274+
kind = pos.getKind() and
1275+
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
1276+
PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and
1277+
matchesCall(ccc, call)
1278+
)
12721279
}
12731280

12741281
/**
@@ -1429,7 +1436,7 @@ private module MkStage<StageSig PrevStage> {
14291436
DataFlowCallable inner
14301437
|
14311438
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
1432-
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
1439+
flowOutOfCall(call, ret, _, out, allowsFieldFlow, config) and
14331440
inner = ret.getEnclosingCallable() and
14341441
ccOut = getCallContextReturn(inner, call, innercc) and
14351442
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1441,11 +1448,11 @@ private module MkStage<StageSig PrevStage> {
14411448
DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, Ap argAp, Ap ap,
14421449
Configuration config
14431450
) {
1444-
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
1451+
exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc |
14451452
fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and
1446-
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
1453+
flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and
14471454
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
1448-
parameterFlowThroughAllowed(summaryCtx, ret.getKind())
1455+
parameterFlowThroughAllowed(summaryCtx, kind)
14491456
)
14501457
}
14511458

@@ -1483,10 +1490,12 @@ private module MkStage<StageSig PrevStage> {
14831490

14841491
pragma[nomagic]
14851492
private predicate returnFlowsThrough(
1486-
RetNodeEx ret, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, Configuration config
1493+
RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap,
1494+
Configuration config
14871495
) {
14881496
fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and
1489-
parameterFlowThroughAllowed(p, ret.getKind())
1497+
pos = ret.getReturnPosition() and
1498+
parameterFlowThroughAllowed(p, pos.getKind())
14901499
}
14911500

14921501
pragma[nomagic]
@@ -1496,7 +1505,7 @@ private module MkStage<StageSig PrevStage> {
14961505
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
14971506
pragma[only_bind_into](config)) and
14981507
fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](config)) and
1499-
returnFlowsThrough(_, _, _, p.asNode(), _, _, pragma[only_bind_into](config))
1508+
returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](config))
15001509
}
15011510

15021511
/**
@@ -1592,13 +1601,15 @@ private module MkStage<StageSig PrevStage> {
15921601
)
15931602
or
15941603
// flow out of a callable
1595-
revFlowOut(_, node, state, _, _, ap, config) and
1596-
if returnFlowsThrough(node, state, _, _, _, ap, config)
1597-
then (
1598-
returnCtx = TReturnCtxMaybeFlowThrough(node.(RetNodeEx).getReturnPosition()) and
1599-
returnAp = apSome(ap)
1600-
) else (
1601-
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
1604+
exists(ReturnPosition pos |
1605+
revFlowOut(_, node, pos, state, _, _, ap, config) and
1606+
if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
1607+
then (
1608+
returnCtx = TReturnCtxMaybeFlowThrough(pos) and
1609+
returnAp = apSome(ap)
1610+
) else (
1611+
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
1612+
)
16021613
)
16031614
}
16041615

@@ -1627,12 +1638,12 @@ private module MkStage<StageSig PrevStage> {
16271638

16281639
pragma[nomagic]
16291640
private predicate revFlowOut(
1630-
DataFlowCall call, RetNodeEx ret, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
1631-
Ap ap, Configuration config
1641+
DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
1642+
ApOption returnAp, Ap ap, Configuration config
16321643
) {
16331644
exists(NodeEx out, boolean allowsFieldFlow |
16341645
revFlow(out, state, returnCtx, returnAp, ap, config) and
1635-
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
1646+
flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and
16361647
if allowsFieldFlow = false then ap instanceof ApNil else any()
16371648
)
16381649
}
@@ -1672,9 +1683,8 @@ private module MkStage<StageSig PrevStage> {
16721683
Configuration config
16731684
) {
16741685
exists(RetNodeEx ret, FlowState state, CcCall ccc |
1675-
revFlowOut(call, ret, state, returnCtx, returnAp, ap, config) and
1676-
returnFlowsThrough(ret, state, ccc, _, _, ap, config) and
1677-
pos = ret.getReturnPosition() and
1686+
revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
1687+
returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
16781688
matchesCall(ccc, call)
16791689
)
16801690
}
@@ -1755,17 +1765,17 @@ private module MkStage<StageSig PrevStage> {
17551765

17561766
pragma[nomagic]
17571767
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
1758-
exists(RetNodeEx ret |
1759-
returnFlowsThrough(ret, _, _, p.asNode(), ap, _, config) and
1760-
parameterFlowsThroughRev(p, ap, ret.getReturnPosition(), config)
1768+
exists(RetNodeEx ret, ReturnPosition pos |
1769+
returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and
1770+
parameterFlowsThroughRev(p, ap, pos, config)
17611771
)
17621772
}
17631773

17641774
pragma[nomagic]
1765-
predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) {
1775+
predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) {
17661776
exists(ParamNodeEx p, Ap ap |
1767-
returnFlowsThrough(ret, _, _, p.asNode(), ap, _, config) and
1768-
parameterFlowsThroughRev(p, ap, ret.getReturnPosition(), config)
1777+
returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and
1778+
parameterFlowsThroughRev(p, ap, pos, config)
17691779
)
17701780
}
17711781

@@ -1946,7 +1956,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
19461956
exists(lcc)
19471957
}
19481958

1949-
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
1959+
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
19501960

19511961
predicate flowIntoCall = flowIntoCallNodeCand1/5;
19521962

@@ -1982,9 +1992,10 @@ private module Stage2 implements StageSig {
19821992

19831993
pragma[nomagic]
19841994
private predicate flowOutOfCallNodeCand2(
1985-
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
1995+
DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow,
1996+
Configuration config
19861997
) {
1987-
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
1998+
flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and
19881999
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
19892000
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
19902001
}
@@ -2053,7 +2064,7 @@ private module LocalFlowBigStep {
20532064
jumpStep(node, next, config) or
20542065
additionalJumpStep(node, next, config) or
20552066
flowIntoCallNodeCand2(_, node, next, _, config) or
2056-
flowOutOfCallNodeCand2(_, node, next, _, config) or
2067+
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
20572068
Stage2::storeStepCand(node, _, _, next, _, config) or
20582069
Stage2::readStepCand(node, _, next, config)
20592070
)
@@ -2194,7 +2205,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
21942205
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
21952206
}
21962207

2197-
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
2208+
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
21982209

21992210
predicate flowIntoCall = flowIntoCallNodeCand2/5;
22002211

@@ -2500,10 +2511,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
25002511

25012512
pragma[nomagic]
25022513
predicate flowOutOfCall(
2503-
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
2514+
DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow,
2515+
Configuration config
25042516
) {
25052517
exists(FlowState state |
2506-
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
2518+
flowOutOfCallNodeCand2(call, node1, pos, node2, allowsFieldFlow, config) and
25072519
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
25082520
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
25092521
pragma[only_bind_into](config))

0 commit comments

Comments
 (0)