diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 5a6de5af0f4..49686124c78 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 5a6de5af0f4..49686124c78 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 5a6de5af0f4..49686124c78 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 5a6de5af0f4..49686124c78 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 5a6de5af0f4..49686124c78 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 5a6de5af0f4..49686124c78 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 5a6de5af0f4..49686124c78 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 5a6de5af0f4..49686124c78 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 5a6de5af0f4..49686124c78 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/java/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql b/java/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql index fd199251201..46818b3efff 100644 --- a/java/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql +++ b/java/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql @@ -40,9 +40,7 @@ class ExtremeSourceFlowConfig extends DataFlow::Configuration { override predicate isSink(DataFlow::Node sink) { sink(_, sink.asExpr()) } - override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSource(node1) and isSource(node2) - } + override predicate isBarrierIn(DataFlow::Node n) { isSource(n) } override predicate isBarrier(DataFlow::Node n) { n.getType() instanceof BooleanType } } diff --git a/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll b/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll index bf27001ba4a..c6dfc4e88ac 100644 --- a/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll +++ b/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll @@ -63,13 +63,23 @@ module TaintTracking { node.asExpr() instanceof ValidatedVariableAccess } - /** Holds if the edge from `node1` to `node2` is a taint sanitizer. */ - predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } + /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ + deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { + deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { isSanitizerEdge(node1, node2) } + /** Holds if data flow into `node` is prohibited. */ + predicate isSanitizerIn(DataFlow::Node node) { none() } + + final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isSanitizerOut(DataFlow::Node node) { none() } + + final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + /** * Holds if the additional taint propagation step from `node1` to `node2` * must be taken into account in the analysis. @@ -135,13 +145,23 @@ module TaintTracking { node.asExpr() instanceof ValidatedVariableAccess } - /** Holds if the edge from `node1` to `node2` is a taint sanitizer. */ - predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } + /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ + deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { + deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { isSanitizerEdge(node1, node2) } + /** Holds if data flow into `node` is prohibited. */ + predicate isSanitizerIn(DataFlow::Node node) { none() } + + final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isSanitizerOut(DataFlow::Node node) { none() } + + final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + /** * Holds if the additional taint propagation step from `node1` to `node2` * must be taken into account in the analysis. diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 5a6de5af0f4..49686124c78 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 5a6de5af0f4..49686124c78 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 5a6de5af0f4..49686124c78 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 5a6de5af0f4..49686124c78 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 5a6de5af0f4..49686124c78 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public * // Optionally override `isAdditionalFlowStep`. * } * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. * * Then, to query whether there is flow between some `source` and `sink`, * write @@ -54,11 +60,20 @@ abstract class Configuration extends string { */ abstract predicate isSink(Node sink); - /** Holds if data flow through `node` is prohibited. */ + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ predicate isBarrier(Node node) { none() } - /** Holds if data flow from `node1` to `node2` is prohibited. */ - predicate isBarrierEdge(Node node1, Node node2) { none() } + /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ + deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } /** * Holds if the additional flow step from `node1` to `node2` must be taken @@ -103,6 +118,26 @@ abstract class Configuration extends string { deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) } } +private predicate inBarrier(Node node, Configuration config) { + config.isBarrierIn(node) and + config.isSource(node) +} + +private predicate outBarrier(Node node, Configuration config) { + config.isBarrierOut(node) and + config.isSink(node) +} + +private predicate fullBarrier(Node node, Configuration config) { + config.isBarrier(node) + or + config.isBarrierIn(node) and + not config.isSource(node) + or + config.isBarrierOut(node) and + not config.isSink(node) +} + private class AdditionalFlowStepSource extends Node { AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) } } @@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep( * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) + localFlowStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` does not jump between callables. */ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { - isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) + isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(Node node1, Node node2, Configuration config) { + jumpStep(node1, node2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) } /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { - exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) | - node2.getEnclosingCallable() != callable1 + exists(DataFlowCallable callable1 | + isAdditionalFlowStep(node1, node2, callable1, config) and + node2.getEnclosingCallable() != callable1 and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) ) } @@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) { * ignoring call contexts. */ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { - not config.isBarrier(node) and + not fullBarrier(node, config) and ( config.isSource(node) and stored = false or @@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) or exists(Node mid | nodeCandFwd1(mid, stored, config) and - jumpStep(mid, node) + jumpStep(mid, node, config) ) or exists(Node mid | @@ -185,7 +244,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, _, node) and - stored = true + stored = true and + not outBarrier(mid, config) ) or // read @@ -193,7 +253,8 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) nodeCandFwd1(mid, true, config) and read(mid, f, node) and storeCandFwd1(f, unbind(config)) and - (stored = false or stored = true) + (stored = false or stored = true) and + not inBarrier(node, config) ) or // flow into a callable @@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) */ private predicate storeCandFwd1(Content f, Configuration config) { exists(Node mid, Node node | - not config.isBarrier(node) and + not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, _, config) and store(mid, f, node) @@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) { ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand1(mid, stored, config) ) or @@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) { ) } +private predicate throughFlowNodeCand(Node node, Configuration config) { + nodeCand1(node, false, config) and + not fullBarrier(node, config) and + not inBarrier(node, config) and + not outBarrier(node, config) +} + /** * Holds if there is a path from `p` to `node` in the same callable that is * part of a path from a source to a sink taking simple call contexts into @@ -329,7 +397,7 @@ pragma[nomagic] private predicate simpleParameterFlow( ParameterNode p, Node node, DataFlowType t, Configuration config ) { - nodeCand1(node, false, config) and + throughFlowNodeCand(node, config) and p = node and t = getErasedRepr(node.getType()) and exists(ReturnNode ret, ReturnKind kind | @@ -338,21 +406,21 @@ private predicate simpleParameterFlow( not parameterValueFlowsThrough(p, kind, _) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localFlowStep(mid, node, config) and compatibleTypes(t, node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, _, config) and additionalLocalFlowStep(mid, node, config) and t = getErasedRepr(node.getType()) ) or - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node mid | simpleParameterFlow(p, mid, t, config) and localStoreReadStep(mid, node) and @@ -360,7 +428,7 @@ private predicate simpleParameterFlow( ) or // value flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, t, config) and argumentValueFlowsThrough(arg, node, _) and @@ -368,7 +436,7 @@ private predicate simpleParameterFlow( ) or // flow through a callable - nodeCand1(node, false, unbind(config)) and + throughFlowNodeCand(node, unbind(config)) and exists(Node arg | simpleParameterFlow(p, arg, _, config) and simpleArgumentFlowsThrough(arg, node, t, config) @@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0( DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config ) { nodeCand1(arg, false, unbind(config)) and + not outBarrier(arg, config) and exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) and kind = ret.getKind() and @@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough( ) { exists(DataFlowCall call, ReturnKind kind | nodeCand1(out, false, unbind(config)) and + not inBarrier(out, config) and simpleArgumentFlowsThrough0(call, arg, kind, t, config) and out = getAnOutNode(call, kind) ) @@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable( private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { nodeCand1(node1, _, unbind(config)) and nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and ( // flow out of an argument exists(ParameterNode p | @@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { viableParamArg(_, node2, node1) and nodeCand1(node1, _, unbind(config)) and - nodeCand1(node2, _, config) + nodeCand1(node2, _, config) and + not outBarrier(node1, config) and + not inBarrier(node2, config) } /** @@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi or exists(Node mid | nodeCandFwd2(mid, _, stored, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and nodeCand2(mid, _, stored, config) and toReturn = false ) @@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) { nodeCand(node, config) and ( config.isSource(node) or - jumpStep(_, node) or + jumpStep(_, node, config) or additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNode or @@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) { */ private predicate localFlowExit(Node node, Configuration config) { exists(Node next | nodeCand(next, config) | - jumpStep(node, next) or + jumpStep(node, next, config) or additionalJumpStep(node, next, config) or flowIntoCallable(node, next, config) or flowOutOfCallable(node, next, config) or @@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, or exists(Node mid | flowCandFwd(mid, _, apf, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flowCand(mid, _, apf, config) and toReturn = false ) @@ -1154,7 +1228,7 @@ private predicate flowFwd0( or exists(Node mid | flowFwd(mid, _, apf, ap, config) and - jumpStep(mid, node) and + jumpStep(mid, node, config) and fromArg = false ) or @@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ) or exists(Node mid | - jumpStep(node, mid) and + jumpStep(node, mid, config) and flow(mid, _, ap, config) and toReturn = false ) @@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() or - jumpStep(mid.getNode(), node) and + jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() or diff --git a/java/ql/test/library-tests/dataflow/inoutbarriers/A.java b/java/ql/test/library-tests/dataflow/inoutbarriers/A.java new file mode 100644 index 00000000000..51604991371 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/inoutbarriers/A.java @@ -0,0 +1,17 @@ +class A { + static String fsrc = ""; + + String src(String s) { return s; } + + void sink(String s) { } + + void foo() { + String s = fsrc; + sink(fsrc); + + s = src(s); + sink(s); + + sink(s); + } +} diff --git a/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected b/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected new file mode 100644 index 00000000000..b219a8c5048 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected @@ -0,0 +1,5 @@ +| A.java:9:16:9:19 | fsrc | A.java:13:10:13:10 | s | nobarrier, sinkbarrier | +| A.java:9:16:9:19 | fsrc | A.java:15:10:15:10 | s | nobarrier | +| A.java:10:10:10:13 | fsrc | A.java:10:10:10:13 | fsrc | both, nobarrier, sinkbarrier, srcbarrier | +| A.java:12:9:12:14 | src(...) | A.java:13:10:13:10 | s | both, nobarrier, sinkbarrier, srcbarrier | +| A.java:12:9:12:14 | src(...) | A.java:15:10:15:10 | s | nobarrier, srcbarrier | diff --git a/java/ql/test/library-tests/dataflow/inoutbarriers/test.ql b/java/ql/test/library-tests/dataflow/inoutbarriers/test.ql new file mode 100644 index 00000000000..03c1290b172 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/inoutbarriers/test.ql @@ -0,0 +1,69 @@ +import java +import semmle.code.java.dataflow.DataFlow +import DataFlow + +predicate src0(Node n) { + n.asExpr().(MethodAccess).getMethod().hasName("src") or + n.asExpr().(FieldAccess).getField().hasName("fsrc") +} + +predicate sink0(Node n) { + exists(MethodAccess sink | + sink.getMethod().hasName("sink") and + sink.getAnArgument() = n.asExpr() + ) +} + +class Conf1 extends Configuration { + Conf1() { this = "inoutbarriers1" } + + override predicate isSource(Node n) { src0(n) } + + override predicate isSink(Node n) { sink0(n) } +} + +class Conf2 extends Configuration { + Conf2() { this = "inoutbarriers2" } + + override predicate isSource(Node n) { src0(n) } + + override predicate isSink(Node n) { sink0(n) } + + override predicate isBarrierIn(Node n) { src0(n) } +} + +class Conf3 extends Configuration { + Conf3() { this = "inoutbarriers3" } + + override predicate isSource(Node n) { src0(n) } + + override predicate isSink(Node n) { sink0(n) } + + override predicate isBarrierOut(Node n) { sink0(n) } +} + +class Conf4 extends Configuration { + Conf4() { this = "inoutbarriers4" } + + override predicate isSource(Node n) { src0(n) } + + override predicate isSink(Node n) { sink0(n) } + + override predicate isBarrierIn(Node n) { src0(n) } + + override predicate isBarrierOut(Node n) { sink0(n) } +} + +predicate flow(Node src, Node sink, string s) { + any(Conf1 c).hasFlow(src, sink) and s = "nobarrier" + or + any(Conf2 c).hasFlow(src, sink) and s = "srcbarrier" + or + any(Conf3 c).hasFlow(src, sink) and s = "sinkbarrier" + or + any(Conf4 c).hasFlow(src, sink) and s = "both" +} + +from Node src, Node sink, string s +where flow(src, sink, _) and s = concat(any(string s0 | flow(src, sink, s0)), ", ") +select src, sink, s