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 f49d975ccf9..9c945f4e83d 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 100ffde2616..e60505d9248 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -328,6 +328,9 @@ private module Cached { cached predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } + cached + predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } + cached predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } 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 f49d975ccf9..9c945f4e83d 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 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 @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 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 @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 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 @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 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 @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 100ffde2616..e60505d9248 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 @@ -328,6 +328,9 @@ private module Cached { cached predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } + cached + predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } + cached predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } 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 f49d975ccf9..9c945f4e83d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 100ffde2616..e60505d9248 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -328,6 +328,9 @@ private module Cached { cached predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } + cached + predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } + cached predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } 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 f49d975ccf9..9c945f4e83d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 100ffde2616..e60505d9248 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -328,6 +328,9 @@ private module Cached { cached predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } + cached + predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } + cached predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } 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 f49d975ccf9..9c945f4e83d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 f49d975ccf9..9c945f4e83d 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() 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 100ffde2616..e60505d9248 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -328,6 +328,9 @@ private module Cached { cached predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } + cached + predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } + cached predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index f49d975ccf9..9c945f4e83d 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll index f49d975ccf9..9c945f4e83d 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll @@ -502,7 +502,7 @@ pragma[inline] private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { exists(ContentSet cs | readSet(node1, cs, node2, config) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } @@ -511,10 +511,22 @@ pragma[inline] private predicate clearsContentEx(NodeEx n, Content c) { exists(ContentSet cs | clearsContentCached(n.asNode(), cs) and - c = cs.getAReadContent() + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() ) } +// inline to reduce fan-out via `getAReadContent` +pragma[inline] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + pragma[nomagic] private predicate store( NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config @@ -793,7 +805,7 @@ private module Stage1 { * by `revFlow`. */ pragma[nomagic] - private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + predicate revFlowIsReadAndStored(Content c, Configuration conf) { revFlowConsCand(c, conf) and revFlowStore(c, _, _, conf) } @@ -891,7 +903,7 @@ private module Stage1 { pragma[nomagic] predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { - revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and revFlow(n2, pragma[only_bind_into](config)) } @@ -1181,11 +1193,26 @@ private module Stage2 { private predicate flowIntoCall = flowIntoCallNodeCand1/5; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + bindingset[node, state, ap, config] private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and - not stateBarrier(node, state, config) + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) } bindingset[ap, contentType] @@ -1740,7 +1767,8 @@ private module LocalFlowBigStep { private class FlowCheckNode extends NodeEx { FlowCheckNode() { castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) } } @@ -1979,6 +2007,16 @@ private module Stage3 { clearContent(node, ap.getHead().getContent(), config) } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + pragma[nomagic] private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } @@ -1987,7 +2025,12 @@ private module Stage3 { exists(state) and exists(config) and not clear(node, ap, config) and - if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) } bindingset[ap, contentType] @@ -4609,6 +4652,10 @@ private module FlowExploration { exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and not fullBarrier(node, config) and not stateBarrier(node, state, config) and distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() @@ -4625,6 +4672,10 @@ private module FlowExploration { not fullBarrier(node, config) and not stateBarrier(node, state, config) and not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and if node.asNode() instanceof CastingNode then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()