diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index ae9c6f3f12e..f981834a6d4 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -915,6 +915,17 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { + string toString() { + this = TParameterPositionNone() and + result = "(none)" + or + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() + ) + } +} + +/** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and + * flow through may be possible. + */ +class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index ae9c6f3f12e..f981834a6d4 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -915,6 +915,17 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { + string toString() { + this = TParameterPositionNone() and + result = "(none)" + or + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() + ) + } +} + +/** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and + * flow through may be possible. + */ +class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index ae9c6f3f12e..f981834a6d4 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -915,6 +915,17 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { + string toString() { + this = TParameterPositionNone() and + result = "(none)" + or + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() + ) + } +} + +/** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and + * flow through may be possible. + */ +class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index ae9c6f3f12e..f981834a6d4 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -915,6 +915,17 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { + string toString() { + this = TParameterPositionNone() and + result = "(none)" + or + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() + ) + } +} + +/** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and + * flow through may be possible. + */ +class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index ae9c6f3f12e..f981834a6d4 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -915,6 +915,17 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { + string toString() { + this = TParameterPositionNone() and + result = "(none)" + or + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() + ) + } +} + +/** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and + * flow through may be possible. + */ +class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll index ae9c6f3f12e..f981834a6d4 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -915,6 +915,17 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { + string toString() { + this = TParameterPositionNone() and + result = "(none)" + or + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() + ) + } +} + +/** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and + * flow through may be possible. + */ +class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll index ae9c6f3f12e..f981834a6d4 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll @@ -915,6 +915,17 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { + string toString() { + this = TParameterPositionNone() and + result = "(none)" + or + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() + ) + } +} + +/** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and + * flow through may be possible. + */ +class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll index 3c41b1876dc..e4cfd5986be 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll @@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx { } ParameterPosition getPosition() { this.isParameterOf(_, result) } - - predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) { ) } +/** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[p, kind] +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) +} + +/** + * Holds if flow from a parameter at position `pos` inside `c` to a return node of + * kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ +bindingset[c, pos, kind] +private predicate parameterFlowThroughAllowed( + DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind +) { + exists(ParamNodeEx p | + p.isParameterOf(c, pos) and + parameterFlowThroughAllowed(p, kind) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +1011,22 @@ private module Stage1 implements StageSig { * candidate for the origin of a summary. */ pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(ReturnKindExt kind | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() - or - p.allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(p, kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + throughFlowNodeCand(ret, config) and + kind = ret.getKind() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and - Stage1::revFlow(ret, config) and - not outBarrier(ret, config) and - not inBarrier(out, config) + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) + ) } pragma[nomagic] @@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1( * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int branch(NodeEx n1, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) { * edge in the graph of paths between sources and sinks that ignores call * contexts. */ +pragma[nomagic] private int join(NodeEx n2, Configuration conf) { result = strictcount(NodeEx n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } @@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(ret, config) and - j = join(out, config) and + b = branch(ret, pragma[only_bind_into](config)) and + j = join(out, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1136,10 +1174,10 @@ pragma[nomagic] private predicate flowIntoCallNodeCand1( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCallNodeCand1(call, arg, p, config) and + flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and exists(int b, int j | - b = branch(arg, config) and - j = join(p, config) and + b = branch(arg, pragma[only_bind_into](config)) and + j = join(p, pragma[only_bind_into](config)) and if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false @@ -1156,7 +1194,9 @@ private signature module StageSig { predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1262,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - matchesCall(ccc, call) + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1262,29 +1303,32 @@ private module MkStage { * configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { - fwdFlow0(node, state, cc, argAp, ap, config) and + fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and filter(node, state, ap, config) } pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { sourceNode(node, state, config) and (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and argAp = apNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and + fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and localCc = getLocalCc(mid, cc) | localStep(mid, state0, node, state, true, _, config, localCc) and @@ -1295,65 +1339,82 @@ private module MkStage { ) or exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) or // store exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and ap = apCons(tc, ap0) ) or // read exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and fwdFlowConsCand(ap0, c, ap, config) ) or // flow into a callable exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and + fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() + if PrevStage::parameterMayFlowThrough(node, apa, config) + then ( + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParameterPositionNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and + flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) + // flow through a callable + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) } pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and typecheckStore(ap1, contentType) ) @@ -1366,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and + fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and tc.getContent() = c and cons = apCons(tc, tail) ) @@ -1374,21 +1435,21 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { - fwdFlow(node1, state, cc, argAp, ap, config) and + fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and getHeadContent(ap) = c } pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and + fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1396,29 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), + TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow, + config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1428,11 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + exists(ParamNodeEx param | + fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and + pos = param.getPosition() ) } @@ -1440,27 +1493,40 @@ private module MkStage { private predicate storeStepFwd( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config) } private predicate readStepFwd( NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and fwdFlowConsCand(ap1, c, ap2, config) } pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config + ) { + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1468,118 +1534,130 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + exists(Ap argAp | + flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow, + pragma[only_bind_into](config)) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + revFlow0(node, state, returnCtx, returnAp, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, + Configuration config ) { - fwdFlow(node, state, _, _, ap, config) and + fwdFlow(node, state, _, _, _, ap, config) and sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + ( + if hasSinkCallCtx(config) + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and returnAp = apNone() and ap instanceof ApNil or exists(NodeEx mid, FlowState state0 | localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) + revFlow(mid, state0, returnCtx, returnAp, ap, config) ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and ap instanceof ApNil ) or exists(NodeEx mid | jumpStep(node, mid, config) and revFlow(mid, state, _, _, ap, config) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() ) or exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStep(node, mid, config) and revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and additionalJumpStateStep(node, state, mid, state0, config) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and + returnCtx = TReturnCtxNone() and returnAp = apNone() and ap instanceof ApNil ) or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and revFlowConsCand(ap0, c, ap, config) ) or // read exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and readStepFwd(node, ap, _, mid, ap0, config) ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and + flowIntoCall(_, node, p, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + // flow through a callable + exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(kind) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) } pragma[nomagic] private predicate revFlowStore( Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config + ReturnCtx returnCtx, ApOption returnAp, Configuration config ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and + revFlow(mid, state, returnCtx, returnAp, ap0, config) and storeStepFwd(node, ap, tc, mid, ap0, config) and tc.getContent() = c } @@ -1599,35 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, + Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(pragma[only_bind_into](p), state, + TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(p, kind) ) } @@ -1638,11 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1784,39 @@ private module MkStage { validAp(ap, config) } - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + revFlow(arg, state, returnCtx, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1754,14 +1824,13 @@ private module MkStage { boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) + count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)) or fwd = false and nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and @@ -1769,8 +1838,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and states = count(FlowState state | revFlow(_, state, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap, config) ) } /* End: Stage logic. */ @@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2091,8 @@ private module LocalFlowBigStep { exists(NodeEx next | Stage2::revFlow(next, state, config) | jumpStep(node, next, config) or additionalJumpStep(node, next, config) or - flowIntoCallNodeCand1(_, node, next, config) or - flowOutOfCallNodeCand1(_, node, next, config) or + flowIntoCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/6; predicate flowIntoCall = flowIntoCallNodeCand2/5; @@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) @@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + c = n.getEnclosingCallable() and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(DataFlowCallable c | - Stage4::parameterMayFlowThrough(_, c, apa, config) and - nodeMayUseSummary0(n, c, state, apa, config) + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -2532,7 +2606,7 @@ private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | - Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and Stage4::revFlow(p, state, _, config) ) } @@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough( ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { - exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + exists(PathNodeMid mid, RetNodeEx ret | pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - pos = sc.getParameterPos() and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - sc.getParamNode().allowParameterReturnInSelf() - ) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll index ae9c6f3f12e..f981834a6d4 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll @@ -915,6 +915,17 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { + string toString() { + this = TParameterPositionNone() and + result = "(none)" + or + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() + ) + } +} + +/** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and + * flow through may be possible. + */ +class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c;