From ca17c5b053e018034d817b0b1fcc51fb46683cb1 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 2 Nov 2022 15:08:28 +0100 Subject: [PATCH 01/10] Data flow: Add summary context to pruning stages 2-4 --- .../ruby/dataflow/internal/DataFlowImpl.qll | 240 +++++++++--------- .../dataflow/internal/DataFlowImplCommon.qll | 18 ++ 2 files changed, 144 insertions(+), 114 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 3c41b1876dc..840a0a46796 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,21 @@ 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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) { + throughFlowNodeCand(ret, config) + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1156,7 +1169,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, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1252,8 +1267,7 @@ private module MkStage { ) { flowOutOfCall(call, ret, 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 + PrevStage::returnMayFlowThrough(ret, pragma[only_bind_into](config)) and matchesCall(ccc, call) } @@ -1262,29 +1276,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1312,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1390,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 +1398,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,13 +1421,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and + fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = ret.getEnclosingCallable() and ccOut = getCallContextReturn(inner, call, innercc) and @@ -1413,12 +1438,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, Ap argAp, Ap ap, + Configuration config ) { exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, ret.getKind()) ) } @@ -1428,11 +1455,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,45 +1469,38 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + parameterFlowThroughAllowed(p, ret.getKind()) + } + + predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) { + returnFlowsThrough(ret, _, _, _, _, _, config) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, p.asNode(), _, _, pragma[only_bind_into](config)) } /** @@ -1494,14 +1516,14 @@ private module MkStage { NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) + fwdFlow(node, state, _, _, _, ap, config) } pragma[nomagic] private predicate revFlow0( NodeEx node, FlowState state, boolean toReturn, 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 returnAp = apNone() and @@ -1513,7 +1535,7 @@ private module MkStage { ) 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 ap instanceof ApNil @@ -1527,7 +1549,7 @@ private module MkStage { ) 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 @@ -1536,7 +1558,7 @@ private module MkStage { ) 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 @@ -1561,6 +1583,7 @@ private module MkStage { revFlowInNotToReturn(node, state, returnAp, ap, config) and toReturn = false or + // flow through a callable exists(DataFlowCall call, Ap returnAp0 | revFlowInToReturn(call, node, state, returnAp0, ap, config) and revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) @@ -1569,7 +1592,7 @@ private module MkStage { // flow out of a callable revFlowOut(_, node, state, _, _, ap, config) and toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) + if returnFlowsThrough(node, state, _, _, _, ap, config) then returnAp = apSome(ap) else returnAp = apNone() } @@ -1642,7 +1665,7 @@ private module MkStage { ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and + returnFlowsThrough(ret, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1721,21 +1744,15 @@ private module MkStage { c = p.getEnclosingCallable() } - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { + exists(DataFlowCallable c, RetNodeEx ret, FlowState state, Ap ap0, 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 + returnFlowsThrough(ret, state, _, p.asNode(), ap, ap0, config) 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() - ) + parameterFlowThroughAllowed(p.asNode(), ret.getKind()) ) } @@ -1754,13 +1771,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -2021,8 +2038,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) ) @@ -2234,7 +2251,8 @@ private predicate flowCandSummaryCtx( ) { exists(AccessPathFront apf | Stage3::revFlow(node, state, true, _, apf, config) and - Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, + config) ) } @@ -2508,13 +2526,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(_, c, _, _) and + Stage4::parameterMayFlowThrough(p, _, _) and Stage4::revFlow(n, state, true, _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and - n.getEnclosingCallable() = c + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2540,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2550,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 +3471,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().asNode(), 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..9923fd955a8 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll @@ -915,6 +915,11 @@ private module Cached { TDataFlowCallNone() or TDataFlowCallSome(DataFlowCall call) + cached + newtype TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1309,19 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParamNode`. */ +class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; From 5adf10fcbab0969d74cd3f36fc2d9e90a179b996 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 2 Nov 2022 20:49:39 +0100 Subject: [PATCH 02/10] Data flow: Add return context to pruning stages 2-4 --- .../ruby/dataflow/internal/DataFlowImpl.qll | 128 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 31 +++++ 2 files changed, 102 insertions(+), 57 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 840a0a46796..8ee4677fc96 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -1489,10 +1489,6 @@ private module MkStage { parameterFlowThroughAllowed(p, ret.getKind()) } - predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) { - returnFlowsThrough(ret, _, _, _, _, _, config) - } - pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config @@ -1507,44 +1503,50 @@ private module MkStage { * 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 + 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 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 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 @@ -1552,7 +1554,7 @@ private module MkStage { 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 ) @@ -1562,47 +1564,50 @@ private module MkStage { 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 + returnCtx = TReturnCtxNone() or // flow through a callable - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) or // flow out of a callable revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and if returnFlowsThrough(node, state, _, _, _, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() + then ( + returnCtx = TReturnCtxMaybeFlowThrough(node.(RetNodeEx).getReturnPosition()) 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 } @@ -1622,11 +1627,11 @@ 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, FlowState state, ReturnCtx returnCtx, ApOption returnAp, + Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and + revFlow(out, state, returnCtx, returnAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1637,7 +1642,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1645,12 +1650,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1661,11 +1668,13 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + revFlowOut(call, ret, state, returnCtx, returnAp, ap, config) and returnFlowsThrough(ret, state, ccc, _, _, ap, config) and + pos = ret.getReturnPosition() and matchesCall(ccc, call) ) } @@ -1736,34 +1745,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } + pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(DataFlowCallable c, RetNodeEx ret, FlowState state, Ap ap0, 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 - returnFlowsThrough(ret, state, _, p.asNode(), ap, ap0, config) and - p.getPosition() = pos and - parameterFlowThroughAllowed(p.asNode(), ret.getKind()) + exists(RetNodeEx ret | + returnFlowsThrough(ret, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, ret.getReturnPosition(), config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, ret.getReturnPosition(), config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1786,8 +1800,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. */ @@ -2250,7 +2264,7 @@ private predicate flowCandSummaryCtx( NodeEx node, FlowState state, AccessPathFront argApf, Configuration config ) { exists(AccessPathFront apf | - Stage3::revFlow(node, state, true, _, apf, config) and + Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf, config) ) @@ -2530,7 +2544,7 @@ private predicate nodeMayUseSummary0( ) { exists(AccessPathApprox apa0 | Stage4::parameterMayFlowThrough(p, _, _) and - Stage4::revFlow(n, state, true, _, apa0, config) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), TAccessPathApproxSome(apa), apa0, config) ) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll index 9923fd955a8..621ed34f9d0 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll @@ -920,6 +920,12 @@ private module Cached { TParamNodeNone() or TParamNodeSome(ParamNode p) + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1322,6 +1328,31 @@ class ParamNodeOption extends TParamNodeOption { } } +/** + * 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(ReturnPosition pos)`: return flow, of kind `pos`, 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(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; From a3a3b46d54695a1164cf369327857b08b6ad79cc Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 7 Nov 2022 11:55:33 +0100 Subject: [PATCH 03/10] Data flow: Account for return nodes with multiple return kinds when restricting flow through For example, flow out via parameters allows for return nodes with multiple return kinds: ```csharp void SetXOrY(C x, C y, bool b) { C c = x; if (b) c = y; c.Field = taint; // post-update node for `c` has two return kinds } ``` --- .../ruby/dataflow/internal/DataFlowImpl.qll | 114 ++++++++++-------- 1 file changed, 63 insertions(+), 51 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 8ee4677fc96..a52ad110662 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -1005,8 +1005,9 @@ private module Stage1 implements StageSig { } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) { - throughFlowNodeCand(ret, config) + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() } pragma[nomagic] @@ -1065,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1103,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1115,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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) ) } @@ -1128,12 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1171,7 +1174,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1237,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1262,13 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pragma[only_bind_into](config)) and - matchesCall(ccc, call) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1429,7 +1436,7 @@ private module MkStage { DataFlowCallable inner | fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, 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() @@ -1441,11 +1448,11 @@ private module MkStage { DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, Ap argAp, Ap ap, Configuration config ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - parameterFlowThroughAllowed(summaryCtx, ret.getKind()) + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1483,10 +1490,12 @@ private module MkStage { pragma[nomagic] private predicate returnFlowsThrough( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, Configuration config + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config ) { fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - parameterFlowThroughAllowed(p, ret.getKind()) + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] @@ -1496,7 +1505,7 @@ private module MkStage { 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](config)) and - returnFlowsThrough(_, _, _, p.asNode(), _, _, pragma[only_bind_into](config)) + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](config)) } /** @@ -1592,13 +1601,15 @@ private module MkStage { ) or // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - if returnFlowsThrough(node, state, _, _, _, ap, config) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(node.(RetNodeEx).getReturnPosition()) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + exists(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) ) } @@ -1627,12 +1638,12 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1672,9 +1683,8 @@ private module MkStage { Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, state, ccc, _, _, ap, config) and - pos = ret.getReturnPosition() and + revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1755,17 +1765,17 @@ private module MkStage { pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret | - returnFlowsThrough(ret, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, ret.getReturnPosition(), config) + exists(RetNodeEx ret, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, ret.getReturnPosition(), config) + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } @@ -1946,7 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1982,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2053,7 +2064,7 @@ private module LocalFlowBigStep { jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallNodeCand2(_, node, next, _, config) or - flowOutOfCallNodeCand2(_, node, next, _, config) or + flowOutOfCallNodeCand2(_, node, _, next, _, config) or Stage2::storeStepCand(node, _, _, next, _, config) or Stage2::readStepCand(node, _, next, config) ) @@ -2194,7 +2205,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; @@ -2500,10 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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)) From 99e70e9a50fdf85f151af20d1ef78f448029beb3 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 2 Nov 2022 21:03:05 +0100 Subject: [PATCH 04/10] Data flow: Sync files --- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 416 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl2.qll | 416 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl3.qll | 416 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl4.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 49 +++ .../cpp/dataflow/internal/DataFlowImpl.qll | 416 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 416 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 416 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 49 +++ .../dataflow/internal/DataFlowImplLocal.qll | 416 ++++++++++-------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 416 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl2.qll | 416 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl3.qll | 416 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl4.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 49 +++ .../csharp/dataflow/internal/DataFlowImpl.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImpl2.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImpl3.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImpl4.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImpl5.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 49 +++ .../DataFlowImplForContentDataFlow.qll | 416 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl.qll | 416 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl2.qll | 416 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl3.qll | 416 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl4.qll | 416 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl5.qll | 416 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl6.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 49 +++ .../DataFlowImplForOnActivityResult.qll | 416 ++++++++++-------- .../DataFlowImplForSerializability.qll | 416 ++++++++++-------- .../dataflow/new/internal/DataFlowImpl.qll | 416 ++++++++++-------- .../dataflow/new/internal/DataFlowImpl2.qll | 416 ++++++++++-------- .../dataflow/new/internal/DataFlowImpl3.qll | 416 ++++++++++-------- .../dataflow/new/internal/DataFlowImpl4.qll | 416 ++++++++++-------- .../new/internal/DataFlowImplCommon.qll | 49 +++ .../ruby/dataflow/internal/DataFlowImpl2.qll | 416 ++++++++++-------- .../DataFlowImplForHttpClientLibraries.qll | 416 ++++++++++-------- .../internal/DataFlowImplForPathname.qll | 416 ++++++++++-------- .../internal/DataFlowImplForRegExp.qll | 416 ++++++++++-------- .../swift/dataflow/internal/DataFlowImpl.qll | 416 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 49 +++ 43 files changed, 8515 insertions(+), 6804 deletions(-) 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..621ed34f9d0 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 TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParamNode`. */ +class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.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(ReturnPosition pos)`: return flow, of kind `pos`, 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(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..621ed34f9d0 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 TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParamNode`. */ +class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.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(ReturnPosition pos)`: return flow, of kind `pos`, 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(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..621ed34f9d0 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 TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParamNode`. */ +class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.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(ReturnPosition pos)`: return flow, of kind `pos`, 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(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..621ed34f9d0 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 TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParamNode`. */ +class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.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(ReturnPosition pos)`: return flow, of kind `pos`, 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(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..621ed34f9d0 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 TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParamNode`. */ +class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.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(ReturnPosition pos)`: return flow, of kind `pos`, 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(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..621ed34f9d0 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 TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParamNode`. */ +class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.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(ReturnPosition pos)`: return flow, of kind `pos`, 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(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.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/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index 3c41b1876dc..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), kind) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll index 3c41b1876dc..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..a52ad110662 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,21 @@ 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(ParamNode p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p) + ) +} + private module Stage1 implements StageSig { class Ap = Unit; @@ -981,21 +994,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.asNode(), kind) ) } + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + throughFlowNodeCand(ret, config) and + pos = ret.getReturnPosition() + } + pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists(ArgNodeEx arg, boolean toReturn | @@ -1052,9 +1066,10 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + viableReturnPosOutNodeCand1(call, pos, out, config) and + pos = ret.getReturnPosition() and Stage1::revFlow(ret, config) and not outBarrier(ret, config) and not inBarrier(out, config) @@ -1090,7 +1105,7 @@ private predicate flowIntoCallNodeCand1( 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) ) } @@ -1102,7 +1117,7 @@ private int branch(NodeEx n1, Configuration conf) { 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 +1130,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, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, ret, out, config) and + flowOutOfCallNodeCand1(call, ret, pos, 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 @@ -1156,7 +1172,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, ReturnPosition pos, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1222,7 +1240,8 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Configuration config ); predicate flowIntoCall( @@ -1247,14 +1266,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow, Configuration config ) { - flowOutOfCall(call, ret, 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) + exists(ReturnPosition pos | + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and + kind = pos.getKind() and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and + matchesCall(ccc, call) + ) } /** @@ -1262,29 +1283,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 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, ParamNodeOption 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, ParamNodeOption 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 = TParamNodeNone() 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 +1319,72 @@ 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeNone() 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 = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argAp = apNone() + ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) 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, ParamNode 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, + ParamNodeOption 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 +1397,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 +1405,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, + ParamNodeOption 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, + ParamNodeOption 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() @@ -1397,14 +1428,15 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, DataFlowCallable inner | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + fwdFlow(ret, state, innercc, summaryCtx, 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() @@ -1413,12 +1445,14 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, 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() + exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + parameterFlowThroughAllowed(summaryCtx, kind) ) } @@ -1428,11 +1462,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, 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 + p = param.asNode() ) } @@ -1440,146 +1476,149 @@ 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, - 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)) - ) + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, + Configuration config + ) { + fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] 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) + 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](config)) and + returnFlowsThrough(_, _, _, _, p.asNode(), _, _, 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 + 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, ReturnPosition returnPos0, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, 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(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap, config) and + if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) 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,12 +1638,12 @@ 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, ReturnPosition pos, 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 + revFlow(out, state, returnCtx, returnAp, ap, config) and + flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1614,7 +1653,7 @@ private module MkStage { ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and + revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and flowIntoCall(_, arg, p, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1622,12 +1661,14 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, Ap returnAp, + Ap ap, Configuration config ) { exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and + revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), 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.asNode(), returnPos.getKind()) ) } @@ -1638,11 +1679,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, ReturnPosition pos, 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, pos, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1713,40 +1755,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, ReturnPosition returnPos, Configuration config ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() + revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and + parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) } - 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, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + exists(ParamNodeEx p, Ap ap | + returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + parameterFlowsThroughRev(p, ap, pos, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + ReturnPosition returnPos0, 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, returnPos0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) ) } @@ -1754,13 +1795,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, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argAp, ap, config) ) or fwd = false and @@ -1769,8 +1810,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 +1956,7 @@ private module Stage2Param implements MkStage::StageParam { exists(lcc) } - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/6; predicate flowIntoCall = flowIntoCallNodeCand1/5; @@ -1951,9 +1992,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { - flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand1(call, node1, pos, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) } @@ -2021,8 +2063,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 +2205,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 +2275,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 +2511,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, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + flowOutOfCallNodeCand2(call, node1, pos, 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 +2552,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, ParamNodeEx p, 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 + Stage4::parameterMayFlowThrough(p, _, _) and + Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + TAccessPathApproxSome(apa), apa0, config) ) } @@ -2522,9 +2566,9 @@ 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(ParamNodeEx p | + Stage4::parameterMayFlowThrough(p, apa, config) and + nodeMayUseSummary0(n, p, state, apa, config) ) } @@ -2532,7 +2576,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 +3497,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().asNode(), 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..621ed34f9d0 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 TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption { } } +/** An optional `ParamNode`. */ +class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.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(ReturnPosition pos)`: return flow, of kind `pos`, 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(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.toString() + ) + } +} + /** A `Content` tagged with the type of a containing object. */ class TypedContent extends MkTypedContent { private Content c; From 4e4ee32dbcfbf65cc298bdf2184e91cc56579f52 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Nov 2022 10:48:29 +0100 Subject: [PATCH 05/10] Data flow: Join on one more column in `flowThroughIntoCall` --- .../codeql/ruby/dataflow/internal/DataFlowImpl.qll | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index a52ad110662..bfdb6c9c9a3 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -1502,10 +1502,13 @@ private module MkStage { private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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.asNode(), pragma[only_bind_into](argAp), _, + pragma[only_bind_into](config)) + ) } /** From 70d2a0df8af532b0422c13c9d46d93085c147d84 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Nov 2022 14:46:59 +0100 Subject: [PATCH 06/10] Data flow: Track parameter position instead of parameter in pruning stages 2-4 --- .../ruby/dataflow/internal/DataFlowImpl.qll | 150 +++++++++++------- .../dataflow/internal/DataFlowImplCommon.qll | 18 +-- 2 files changed, 105 insertions(+), 63 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index bfdb6c9c9a3..13d7687c188 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,7 +1017,7 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } @@ -1102,6 +1119,7 @@ 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 | @@ -1114,6 +1132,7 @@ 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 | @@ -1152,10 +1171,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 @@ -1266,15 +1285,16 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { exists(ReturnPosition pos | flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and kind = pos.getKind() and PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() ) } @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,9 +1382,10 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or @@ -1372,7 +1393,7 @@ private module MkStage { fwdFlowOutNotFromArg(node, state, cc, summaryCtx, argAp, ap, config) or // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1402,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1427,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1437,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1428,8 +1449,8 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx out, FlowState state, Cc ccOut, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { exists( DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, @@ -1445,14 +1466,18 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, ParamNode summaryCtx, Ap argAp, Ap ap, + DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap, Configuration config ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + exists( + DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc + | + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1487,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,13 +1514,29 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, FlowState state, CcCall ccc, DataFlowCallable c, ParameterPosition ppos, + Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + exists(boolean allowsFieldFlow | + fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), _, _, allowsFieldFlow, + pragma[only_bind_into](config)) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + Ap ap, Configuration config + ) { + exists(DataFlowCallable c, ParameterPosition ppos | + returnFlowsThrough0(ret, state, ccc, c, ppos, argAp, ap, config) and + p.isParameterOf(c, ppos) and + pos = ret.getReturnPosition() and + parameterFlowThroughAllowed(p, pos.getKind()) + ) } pragma[nomagic] @@ -1506,7 +1547,7 @@ private module MkStage { 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.asNode(), pragma[only_bind_into](argAp), _, + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _, pragma[only_bind_into](config)) ) } @@ -1671,7 +1712,7 @@ private module MkStage { revFlow(p, state, TReturnCtxMaybeFlowThrough(returnPos), apSome(returnAp), ap, config) and flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, returnPos.getKind()) ) } @@ -1763,13 +1804,13 @@ private module MkStage { ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config ) { revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, returnPos.getKind()) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and parameterFlowsThroughRev(p, ap, pos, config) ) } @@ -1777,7 +1818,7 @@ private module MkStage { pragma[nomagic] predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and + returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and parameterFlowsThroughRev(p, ap, pos, config) ) } @@ -1803,9 +1844,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -2555,12 +2595,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2569,9 +2610,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3504,7 +3546,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 621ed34f9d0..6a79e7eb6ce 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll @@ -916,9 +916,9 @@ private module Cached { TDataFlowCallSome(DataFlowCall call) cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) cached newtype TReturnCtx = @@ -1315,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption { } } -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { string toString() { - this = TParamNodeNone() and + this = TParameterPositionNone() and result = "(none)" or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() ) } } From 4346a7f426f9f8781a97e5509596ad454d8d74bb Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Nov 2022 14:48:30 +0100 Subject: [PATCH 07/10] Data flow: Inline `fwdFlowOutNotFromArg` --- .../ruby/dataflow/internal/DataFlowImpl.qll | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 13d7687c188..911f2299bcc 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -1390,7 +1390,16 @@ private module MkStage { ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | @@ -1447,23 +1456,6 @@ private module MkStage { ) } - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParameterPositionOption summaryCtx, ApOption argAp, - Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, 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, ParameterPosition summaryCtx, Ap argAp, Ap ap, From bdb205a318618c4136cf0cfb116d98711c9250e9 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Nov 2022 15:03:38 +0100 Subject: [PATCH 08/10] Data flow: Track return kind instead of return position in pruning stages 2-4 --- .../ruby/dataflow/internal/DataFlowImpl.qll | 118 +++++++++--------- .../dataflow/internal/DataFlowImplCommon.qll | 10 +- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 911f2299bcc..89be164852f 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -1022,9 +1022,9 @@ private module Stage1 implements StageSig { } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1083,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1149,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1193,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1259,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1288,14 +1291,11 @@ private module MkStage { DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) and - c = ret.getEnclosingCallable() - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1507,12 +1507,12 @@ private module MkStage { pragma[nomagic] private predicate returnFlowsThrough0( - RetNodeEx ret, FlowState state, CcCall ccc, DataFlowCallable c, ParameterPosition ppos, - Ap argAp, Ap ap, Configuration config + 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), _, _, allowsFieldFlow, + flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow, pragma[only_bind_into](config)) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1520,14 +1520,13 @@ private module MkStage { pragma[nomagic] private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp, Ap ap, Configuration config ) { exists(DataFlowCallable c, ParameterPosition ppos | - returnFlowsThrough0(ret, state, ccc, c, ppos, argAp, ap, config) and + returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and p.isParameterOf(c, ppos) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1631,17 +1630,17 @@ private module MkStage { returnCtx = TReturnCtxNone() or // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1674,12 +1673,12 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, config) and + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -1697,14 +1696,15 @@ private module MkStage { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p, returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1715,12 +1715,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1793,37 +1793,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p, returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -2027,10 +2027,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2546,11 +2546,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll index 6a79e7eb6ce..f981834a6d4 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll @@ -924,7 +924,7 @@ private module Cached { newtype TReturnCtx = TReturnCtxNone() or TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1335,7 +1335,7 @@ class ParameterPositionOption extends TParameterPositionOption { * * - `TReturnCtxNone()`: no return flow. * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and * flow through may be possible. */ class ReturnCtx extends TReturnCtx { @@ -1346,9 +1346,9 @@ class ReturnCtx extends TReturnCtx { this = TReturnCtxNoFlowThrough() and result = "(no flow through)" or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() ) } } From c65780ee99cd015e496ee17aa3e9a3f732b7bb50 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Nov 2022 15:06:54 +0100 Subject: [PATCH 09/10] Data flow: Inline `revFlowInNotToReturn` --- .../ruby/dataflow/internal/DataFlowImpl.qll | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 89be164852f..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -1626,8 +1626,12 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 | @@ -1683,17 +1687,6 @@ private module MkStage { ) } - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - pragma[nomagic] private predicate revFlowInToReturn( DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, From cde05e11907827d753141da89d8f22cf7a267812 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Nov 2022 15:07:45 +0100 Subject: [PATCH 10/10] Data flow: Sync files --- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 292 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl2.qll | 292 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl3.qll | 292 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl4.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 28 +- .../cpp/dataflow/internal/DataFlowImpl.qll | 292 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 292 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 292 ++++++++++-------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 28 +- .../dataflow/internal/DataFlowImplLocal.qll | 292 ++++++++++-------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 292 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl2.qll | 292 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl3.qll | 292 ++++++++++-------- .../ir/dataflow/internal/DataFlowImpl4.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 28 +- .../csharp/dataflow/internal/DataFlowImpl.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImpl2.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImpl3.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImpl4.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImpl5.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 28 +- .../DataFlowImplForContentDataFlow.qll | 292 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl.qll | 292 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl2.qll | 292 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl3.qll | 292 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl4.qll | 292 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl5.qll | 292 ++++++++++-------- .../java/dataflow/internal/DataFlowImpl6.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 28 +- .../DataFlowImplForOnActivityResult.qll | 292 ++++++++++-------- .../DataFlowImplForSerializability.qll | 292 ++++++++++-------- .../dataflow/new/internal/DataFlowImpl.qll | 292 ++++++++++-------- .../dataflow/new/internal/DataFlowImpl2.qll | 292 ++++++++++-------- .../dataflow/new/internal/DataFlowImpl3.qll | 292 ++++++++++-------- .../dataflow/new/internal/DataFlowImpl4.qll | 292 ++++++++++-------- .../new/internal/DataFlowImplCommon.qll | 28 +- .../ruby/dataflow/internal/DataFlowImpl2.qll | 292 ++++++++++-------- .../DataFlowImplForHttpClientLibraries.qll | 292 ++++++++++-------- .../internal/DataFlowImplForPathname.qll | 292 ++++++++++-------- .../internal/DataFlowImplForRegExp.qll | 292 ++++++++++-------- .../swift/dataflow/internal/DataFlowImpl.qll | 292 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 28 +- 43 files changed, 5894 insertions(+), 4814 deletions(-) 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 a52ad110662..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 @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..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 @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..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 @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..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 @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 621ed34f9d0..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 @@ -916,15 +916,15 @@ private module Cached { TDataFlowCallSome(DataFlowCall call) cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) cached newtype TReturnCtx = TReturnCtxNone() or TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1315,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption { } } -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { string toString() { - this = TParamNodeNone() and + this = TParameterPositionNone() and result = "(none)" or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() ) } } @@ -1335,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption { * * - `TReturnCtxNone()`: no return flow. * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and * flow through may be possible. */ class ReturnCtx extends TReturnCtx { @@ -1346,9 +1346,9 @@ class ReturnCtx extends TReturnCtx { this = TReturnCtxNoFlowThrough() and result = "(no flow through)" or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() ) } } 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 a52ad110662..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 621ed34f9d0..f981834a6d4 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -916,15 +916,15 @@ private module Cached { TDataFlowCallSome(DataFlowCall call) cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) cached newtype TReturnCtx = TReturnCtxNone() or TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1315,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption { } } -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { string toString() { - this = TParamNodeNone() and + this = TParameterPositionNone() and result = "(none)" or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() ) } } @@ -1335,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption { * * - `TReturnCtxNone()`: no return flow. * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and * flow through may be possible. */ class ReturnCtx extends TReturnCtx { @@ -1346,9 +1346,9 @@ class ReturnCtx extends TReturnCtx { this = TReturnCtxNoFlowThrough() and result = "(no flow through)" or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() ) } } 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 a52ad110662..e4cfd5986be 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..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 @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..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 @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..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 @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..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 @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 621ed34f9d0..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 @@ -916,15 +916,15 @@ private module Cached { TDataFlowCallSome(DataFlowCall call) cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) cached newtype TReturnCtx = TReturnCtxNone() or TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1315,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption { } } -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { string toString() { - this = TParamNodeNone() and + this = TParameterPositionNone() and result = "(none)" or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() ) } } @@ -1335,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption { * * - `TReturnCtxNone()`: no return flow. * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and * flow through may be possible. */ class ReturnCtx extends TReturnCtx { @@ -1346,9 +1346,9 @@ class ReturnCtx extends TReturnCtx { this = TReturnCtxNoFlowThrough() and result = "(no flow through)" or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() ) } } 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 a52ad110662..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 621ed34f9d0..f981834a6d4 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -916,15 +916,15 @@ private module Cached { TDataFlowCallSome(DataFlowCall call) cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) cached newtype TReturnCtx = TReturnCtxNone() or TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1315,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption { } } -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { string toString() { - this = TParamNodeNone() and + this = TParameterPositionNone() and result = "(none)" or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() ) } } @@ -1335,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption { * * - `TReturnCtxNone()`: no return flow. * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and * flow through may be possible. */ class ReturnCtx extends TReturnCtx { @@ -1346,9 +1346,9 @@ class ReturnCtx extends TReturnCtx { this = TReturnCtxNoFlowThrough() and result = "(no flow through)" or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() ) } } 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 a52ad110662..e4cfd5986be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 621ed34f9d0..f981834a6d4 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -916,15 +916,15 @@ private module Cached { TDataFlowCallSome(DataFlowCall call) cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) cached newtype TReturnCtx = TReturnCtxNone() or TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1315,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption { } } -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { string toString() { - this = TParamNodeNone() and + this = TParameterPositionNone() and result = "(none)" or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() ) } } @@ -1335,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption { * * - `TReturnCtxNone()`: no return flow. * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and * flow through may be possible. */ class ReturnCtx extends TReturnCtx { @@ -1346,9 +1346,9 @@ class ReturnCtx extends TReturnCtx { this = TReturnCtxNoFlowThrough() and result = "(no flow through)" or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() ) } } 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 a52ad110662..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 621ed34f9d0..f981834a6d4 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -916,15 +916,15 @@ private module Cached { TDataFlowCallSome(DataFlowCall call) cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) cached newtype TReturnCtx = TReturnCtxNone() or TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1315,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption { } } -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { string toString() { - this = TParamNodeNone() and + this = TParameterPositionNone() and result = "(none)" or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() ) } } @@ -1335,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption { * * - `TReturnCtxNone()`: no return flow. * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and * flow through may be possible. */ class ReturnCtx extends TReturnCtx { @@ -1346,9 +1346,9 @@ class ReturnCtx extends TReturnCtx { this = TReturnCtxNoFlowThrough() and result = "(no flow through)" or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() ) } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index a52ad110662..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + parameterFlowThroughAllowed(sc.getParamNode(), kind) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll index a52ad110662..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 a52ad110662..e4cfd5986be 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll @@ -613,11 +613,28 @@ private predicate hasSinkCallCtx(Configuration config) { * explicitly allowed */ bindingset[p, kind] -private predicate parameterFlowThroughAllowed(ParamNode p, ReturnKindExt kind) { +private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { exists(ParameterPosition pos | p.isParameterOf(_, pos) | not kind.(ParamUpdateReturnKind).getPosition() = pos or - allowParameterReturnInSelfCached(p) + 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) ) } @@ -1000,14 +1017,14 @@ private module Stage1 implements StageSig { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - parameterFlowThroughAllowed(p.asNode(), kind) + parameterFlowThroughAllowed(p, kind) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { throughFlowNodeCand(ret, config) and - pos = ret.getReturnPosition() + kind = ret.getKind() } pragma[nomagic] @@ -1066,13 +1083,16 @@ private predicate viableReturnPosOutNodeCand1( */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Configuration config + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config ) { - viableReturnPosOutNodeCand1(call, pos, out, config) and - pos = ret.getReturnPosition() 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] @@ -1102,6 +1122,7 @@ 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 | @@ -1114,6 +1135,7 @@ 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 | @@ -1130,10 +1152,10 @@ private int join(NodeEx n2, Configuration conf) { */ pragma[nomagic] private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, ret, pos, out, pragma[only_bind_into](config)) and + flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and exists(int b, int j | b = branch(ret, pragma[only_bind_into](config)) and j = join(out, pragma[only_bind_into](config)) and @@ -1152,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 @@ -1174,7 +1196,7 @@ private signature module StageSig { predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config); + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config); predicate storeStepCand( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, @@ -1240,7 +1262,7 @@ private module MkStage { ); predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, Configuration config ); @@ -1266,16 +1288,14 @@ private module MkStage { pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow, Configuration config + DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind, + NodeEx out, boolean allowsFieldFlow, Configuration config ) { - exists(ReturnPosition pos | - flowOutOfCall(call, ret, pos, out, allowsFieldFlow, pragma[only_bind_into](config)) and - kind = pos.getKind() and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::returnMayFlowThrough(ret, pos, pragma[only_bind_into](config)) and - matchesCall(ccc, call) - ) + flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and + matchesCall(ccc, call) and + c = ret.getEnclosingCallable() } /** @@ -1284,12 +1304,12 @@ private module MkStage { * * The call context `cc` records whether the node is reached through an * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter and access path of that argument, respectively. + * corresponding parameter position and access path of that argument, respectively. */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, - Configuration config + NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + Ap ap, Configuration config ) { fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and @@ -1298,13 +1318,13 @@ private module MkStage { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, 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 = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and ap = getApNil(node) or exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | @@ -1322,7 +1342,7 @@ private module MkStage { fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and jumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() ) or @@ -1330,7 +1350,7 @@ private module MkStage { fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStep(mid, node, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1339,7 +1359,7 @@ private module MkStage { fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and additionalJumpStateStep(mid, state0, node, state, config) and cc = ccNone() and - summaryCtx = TParamNodeNone() and + summaryCtx = TParameterPositionNone() and argAp = apNone() and ap = getApNil(node) ) @@ -1362,17 +1382,27 @@ private module MkStage { apa = getApprox(ap) and if PrevStage::parameterMayFlowThrough(node, apa, config) then ( - summaryCtx = TParamNodeSome(node.asNode()) and argAp = apSome(ap) + summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and + argAp = apSome(ap) ) else ( - summaryCtx = TParamNodeNone() and argAp = apNone() + summaryCtx = TParameterPositionNone() and argAp = apNone() ) ) or // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, summaryCtx, 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 // flow through a callable - exists(DataFlowCall call, ParamNode summaryCtx0, Ap argAp0 | + exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 | fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config) ) @@ -1381,7 +1411,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowStore( NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { exists(DataFlowType contentType | fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and @@ -1406,7 +1436,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowRead( Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, ApOption argAp, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Configuration config ) { fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and PrevStage::readStepCand(node1, c, node2, config) and @@ -1416,7 +1446,7 @@ private module MkStage { pragma[nomagic] private predicate fwdFlowIn( DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, - ParamNodeOption summaryCtx, ApOption argAp, Ap ap, Configuration config + ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config ) { exists(ArgNodeEx arg, boolean allowsFieldFlow | fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and @@ -1427,32 +1457,19 @@ private module MkStage { } pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ParamNodeOption summaryCtx, ApOption argAp, Ap ap, + 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, summaryCtx, 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, ParamNode summaryCtx, Ap argAp, Ap ap, - Configuration config - ) { - exists(RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, TParamNodeSome(summaryCtx), apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, kind, out, allowsFieldFlow, config) and + 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(summaryCtx, kind) + parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind) ) } @@ -1462,13 +1479,13 @@ private module MkStage { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ParamNode p, Ap ap, - Configuration config + DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp, + ParameterPosition pos, Ap ap, Configuration config ) { exists(ParamNodeEx param | fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and - p = param.asNode() + pos = param.getPosition() ) } @@ -1489,23 +1506,41 @@ private module MkStage { } pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNode p, Ap argAp, Ap ap, - Configuration config + private predicate returnFlowsThrough0( + RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c, + ParameterPosition ppos, Ap argAp, Ap ap, Configuration config ) { - fwdFlow(ret, state, ccc, TParamNodeSome(p), apSome(argAp), ap, config) and - pos = ret.getReturnPosition() and - parameterFlowThroughAllowed(p, pos.getKind()) + 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 + 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) + ) } pragma[nomagic] private predicate flowThroughIntoCall( DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config ) { - 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](config)) and - returnFlowsThrough(_, _, _, _, p.asNode(), _, _, pragma[only_bind_into](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)) + ) } /** @@ -1591,21 +1626,25 @@ private module MkStage { ) or // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - returnCtx = TReturnCtxNone() + 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 // flow through a callable - exists(DataFlowCall call, ReturnPosition returnPos0, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + 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 - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap, config) and - if returnFlowsThrough(node, pos, state, _, _, _, ap, config) + exists(ReturnKindExt kind | + revFlowOut(_, node, kind, state, _, _, ap, config) and + if returnFlowsThrough(node, kind, state, _, _, _, ap, config) then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnCtx = TReturnCtxMaybeFlowThrough(kind) and returnAp = apSome(ap) ) else ( returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() @@ -1638,37 +1677,27 @@ private module MkStage { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap, Configuration config ) { exists(NodeEx out, boolean allowsFieldFlow | revFlow(out, state, returnCtx, returnAp, ap, config) and - flowOutOfCall(call, ret, pos, 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, TReturnCtxNone(), returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, 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, ReturnPosition returnPos, 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, TReturnCtxMaybeFlowThrough(returnPos), 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()) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + parameterFlowThroughAllowed(p, kind) ) } @@ -1679,12 +1708,12 @@ private module MkStage { */ pragma[nomagic] private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap, + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap, Configuration config ) { exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and - returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and + revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and + returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and matchesCall(ccc, call) ) } @@ -1757,37 +1786,37 @@ private module MkStage { pragma[nomagic] private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition returnPos, Configuration config + ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(returnPos), apSome(_), ap, config) and - parameterFlowThroughAllowed(p.asNode(), returnPos.getKind()) + revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and + parameterFlowThroughAllowed(p, kind) } pragma[nomagic] predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) { - exists(RetNodeEx ret, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + exists(RetNodeEx ret, ReturnKindExt kind | + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnPosition pos, Configuration config) { + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) { exists(ParamNodeEx p, Ap ap | - returnFlowsThrough(ret, pos, _, _, p.asNode(), ap, _, config) and - parameterFlowsThroughRev(p, ap, pos, config) + returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and + parameterFlowsThroughRev(p, ap, kind, config) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { exists( - ReturnPosition returnPos0, Ap returnAp0, ArgNodeEx arg, FlowState state, + ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnPos0, returnAp0, ap, config) and - revFlowIsReturned(call, returnCtx, returnAp, returnPos0, returnAp0, config) + revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and + revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config) ) } @@ -1800,9 +1829,8 @@ private module MkStage { conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, summaryCtx, 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 @@ -1992,10 +2020,10 @@ private module Stage2 implements StageSig { pragma[nomagic] private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { - flowOutOfCallNodeCand1(call, node1, pos, 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)) } @@ -2511,11 +2539,11 @@ private module Stage4Param implements MkStage::StageParam { pragma[nomagic] predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnPosition pos, NodeEx node2, boolean allowsFieldFlow, + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, pos, 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)) @@ -2552,12 +2580,13 @@ private Configuration unbindConf(Configuration conf) { pragma[nomagic] private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config + NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa, + Configuration config ) { exists(AccessPathApprox apa0 | - Stage4::parameterMayFlowThrough(p, _, _) and + c = n.getEnclosingCallable() and Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and - Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos), TAccessPathApproxSome(apa), apa0, config) ) } @@ -2566,9 +2595,10 @@ pragma[nomagic] private predicate nodeMayUseSummary( NodeEx n, FlowState state, AccessPathApprox apa, Configuration config ) { - exists(ParamNodeEx p | + exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p | Stage4::parameterMayFlowThrough(p, apa, config) and - nodeMayUseSummary0(n, p, state, apa, config) + nodeMayUseSummary0(n, c, pos, state, apa, config) and + p.isParameterOf(c, pos) ) } @@ -3501,7 +3531,7 @@ private predicate paramFlowsThrough( pathNode(mid, ret, state, cc, sc, ap, config, _) and kind = ret.getKind() and apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode().asNode(), kind) + 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 621ed34f9d0..f981834a6d4 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll @@ -916,15 +916,15 @@ private module Cached { TDataFlowCallSome(DataFlowCall call) cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) + newtype TParameterPositionOption = + TParameterPositionNone() or + TParameterPositionSome(ParameterPosition pos) cached newtype TReturnCtx = TReturnCtxNone() or TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) + TReturnCtxMaybeFlowThrough(ReturnKindExt kind) cached newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } @@ -1315,15 +1315,15 @@ class DataFlowCallOption extends TDataFlowCallOption { } } -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { +/** An optional `ParameterPosition`. */ +class ParameterPositionOption extends TParameterPositionOption { string toString() { - this = TParamNodeNone() and + this = TParameterPositionNone() and result = "(none)" or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() + exists(ParameterPosition pos | + this = TParameterPositionSome(pos) and + result = pos.toString() ) } } @@ -1335,7 +1335,7 @@ class ParamNodeOption extends TParamNodeOption { * * - `TReturnCtxNone()`: no return flow. * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and * flow through may be possible. */ class ReturnCtx extends TReturnCtx { @@ -1346,9 +1346,9 @@ class ReturnCtx extends TReturnCtx { this = TReturnCtxNoFlowThrough() and result = "(no flow through)" or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() + exists(ReturnKindExt kind | + this = TReturnCtxMaybeFlowThrough(kind) and + result = kind.toString() ) } }