diff --git a/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll b/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll new file mode 100644 index 00000000000..effd3b5c9ad --- /dev/null +++ b/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll @@ -0,0 +1,66 @@ +/** Contains predicates for dealing with container flow. */ + +import go +private import DataFlowNodes +private import DataFlowPrivate +private import DataFlowUtil + +/** + * Holds if the step from `node1` to `node2` stores a value in a slice or array. + * This covers array assignments and initializers as well as implicit array + * creations for varargs. + */ +predicate containerStoreStep(Node node1, Node node2, Content c) { + c instanceof ArrayContent and + ( + // currently there is no database information about variadic functions + ( + node1.getType() instanceof ArrayType or + node1.getType() instanceof SliceType + ) and + exists(Write w | w.writesElement(node1, _, node2)) + ) + or + c instanceof CollectionContent and + exists(BinaryOperationNode send | send.hasOperands(node1, node2) | send.getOperator() = "<-") + or + c instanceof MapKeyContent and + node1.getType() instanceof MapType and + exists(Write w | w.writesElement(node1, node2, _)) + or + c instanceof MapValueContent and + node1.getType() instanceof MapType and + exists(Write w | w.writesElement(node1, _, node2)) +} + +/** + * Holds if the step from `node1` to `node2` reads a value from a slice or array. + * This covers ordinary array reads as well as array iteration through enhanced + * `for` statements. + */ +predicate containerReadStep(Node node1, Node node2, Content c) { + c instanceof ArrayContent and + ( + node2.(Read).readsElement(node1, _) and + ( + node1.getType() instanceof ArrayType or + node1.getType() instanceof SliceType + ) + or + node2.(RangeElementNode).getBase() = node1 + ) + or + c instanceof CollectionContent and + exists(UnaryOperationNode recv | recv = node2 | + node2 = recv.getOperand() and + recv.getOperator() = "<-" + ) + or + c instanceof MapKeyContent and + node1.getType() instanceof MapType and + node2.(RangeIndexNode).getBase() = node1 + or + c instanceof MapValueContent and + node1.getType() instanceof MapType and + node2.(Read).readsElement(node1, _) +} diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll index 18602f2ed72..5bc1ef5e8e1 100644 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll +++ b/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll @@ -1,6 +1,7 @@ private import go private import DataFlowUtil private import DataFlowImplCommon +private import ContainerFlow private import FlowSummaryImpl as FlowSummaryImpl import DataFlowNodes::Private @@ -125,6 +126,8 @@ predicate storeStep(Node node1, Content c, PostUpdateNode node2) { c = any(DataFlow::PointerContent pc | pc.getPointerType() = node2.getType()) or FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) + or + containerStoreStep(node1, node2, c) } /** @@ -143,6 +146,8 @@ predicate readStep(Node node1, Content f, Node node2) { ) or FlowSummaryImpl::Private::Steps::summaryReadStep(node1, f, node2) + or + containerReadStep(node1, node2, f) } /**