diff --git a/ql/src/semmle/go/dataflow/FunctionInputsAndOutputs.qll b/ql/src/semmle/go/dataflow/FunctionInputsAndOutputs.qll index 95a4c180b97..1cf73dad3e8 100644 --- a/ql/src/semmle/go/dataflow/FunctionInputsAndOutputs.qll +++ b/ql/src/semmle/go/dataflow/FunctionInputsAndOutputs.qll @@ -4,6 +4,7 @@ */ import go +private import semmle.go.dataflow.internal.DataFlowPrivate /** * An abstract representation of an input to a function, which is either a parameter @@ -181,8 +182,8 @@ private class OutParameter extends FunctionOutput, TOutParameter { } override DataFlow::Node getExitNode(DataFlow::CallNode c) { - exists(DataFlow::ArgumentNode arg | - arg.argumentOf(c.asExpr(), index) and + exists(DataFlow::Node arg | + arg = getArgument(c, index) and result.(DataFlow::PostUpdateNode).getPreUpdateNode() = arg ) } diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll b/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll index 16b85fa8aef..049bfb2667e 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll @@ -272,3 +272,14 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { } int accessPathLimit() { result = 5 } + +/** + * Gets the `i`th argument of call `c`, where the receiver of a method call + * counts as argument -1. + */ +Node getArgument(CallNode c, int i) { + result = c.getArgument(i) + or + result = c.(MethodCallNode).getReceiver() and + i = -1 +} diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll b/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll index 09e396a9563..3645073cf2f 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll @@ -4,6 +4,7 @@ import go import semmle.go.dataflow.FunctionInputsAndOutputs +private import DataFlowPrivate cached private newtype TNode = @@ -423,17 +424,6 @@ class PostUpdateNode extends Node { Node getPreUpdateNode() { result = preupd } } -/** - * Gets the `i`th argument of call `c`, where the receiver of a method call - * counts as argument -1. - */ -private Node getArgument(CallNode c, int i) { - result = c.getArgument(i) - or - result = c.(MethodCallNode).getReceiver() and - i = -1 -} - /** * A data-flow node that occurs as an argument in a call, including receiver arguments. */ diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getEntryNode.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getEntryNode.expected index 316a5f82f61..2989f4ef314 100644 --- a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getEntryNode.expected +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getEntryNode.expected @@ -2,6 +2,7 @@ | parameter 0 | main.go:53:2:53:22 | call to op2 | main.go:53:6:53:8 | "-" | | parameter 0 | main.go:55:2:55:27 | call to Printf | main.go:55:13:55:20 | "%d, %d" | | parameter 0 | main.go:57:2:57:27 | call to Printf | main.go:57:13:57:20 | "%d, %d" | +| parameter 0 | tst.go:10:2:10:29 | call to ReadFrom | tst.go:10:23:10:28 | reader | | parameter 1 | main.go:51:2:51:14 | call to op | main.go:51:10:51:10 | 1 | | parameter 1 | main.go:53:2:53:22 | call to op2 | main.go:53:11:53:11 | 2 | | parameter 1 | main.go:55:2:55:27 | call to Printf | main.go:55:23:55:23 | x | @@ -11,3 +12,4 @@ | parameter 2 | main.go:55:2:55:27 | call to Printf | main.go:55:26:55:26 | y | | parameter 2 | main.go:57:2:57:27 | call to Printf | main.go:57:26:57:26 | y | | receiver | main.go:53:14:53:21 | call to bump | main.go:53:14:53:14 | c | +| receiver | tst.go:10:2:10:29 | call to ReadFrom | tst.go:10:2:10:12 | bytesBuffer | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected index 92e681a7b01..f13c3f16bd1 100644 --- a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected @@ -1,6 +1,7 @@ | parameter 0 | main.go:5:1:11:1 | function declaration | main.go:5:9:5:10 | definition of op | | parameter 0 | main.go:13:1:20:1 | function declaration | main.go:13:10:13:11 | definition of op | | parameter 0 | main.go:40:1:48:1 | function declaration | main.go:40:12:40:12 | definition of b | +| parameter 0 | tst.go:8:1:11:1 | function declaration | tst.go:8:12:8:17 | definition of reader | | parameter 1 | main.go:5:1:11:1 | function declaration | main.go:5:20:5:20 | definition of x | | parameter 1 | main.go:13:1:20:1 | function declaration | main.go:13:21:13:21 | definition of x | | parameter 2 | main.go:5:1:11:1 | function declaration | main.go:5:27:5:27 | definition of y | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.expected new file mode 100644 index 00000000000..0c64f6cc4b4 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.expected @@ -0,0 +1 @@ +| file://:0:0:0:0 | ReadFrom | tst.go:10:23:10:28 | reader | tst.go:9:2:9:12 | definition of bytesBuffer | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.ql b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.ql new file mode 100644 index 00000000000..d21a051038d --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.ql @@ -0,0 +1,13 @@ +import go + +class BytesReadFrom extends TaintTracking::FunctionModel, Method { + BytesReadFrom() { this.(Method).hasQualifiedName("bytes", "Buffer", "ReadFrom") } + + override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { + inp.isParameter(0) and outp.isReceiver() + } +} + +from Function fn, DataFlow::Node pred, DataFlow::Node succ +where TaintTracking::functionModelStep(fn, pred, succ) +select fn, pred, succ diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getExitNode.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getExitNode.expected index 19c2533a831..f84d4616a8f 100644 --- a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getExitNode.expected +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getExitNode.expected @@ -1,6 +1,8 @@ +| receiver | tst.go:10:2:10:29 | call to ReadFrom | tst.go:9:2:9:12 | definition of bytesBuffer | | result | main.go:51:2:51:14 | call to op | main.go:51:2:51:14 | call to op | | result | main.go:53:2:53:22 | call to op2 | main.go:53:2:53:22 | call to op2 | | result | main.go:53:14:53:21 | call to bump | main.go:53:14:53:21 | call to bump | +| result | tst.go:9:17:9:33 | call to new | tst.go:9:17:9:33 | call to new | | result 0 | main.go:54:10:54:15 | call to test | main.go:54:2:54:15 | ... := ...[0] | | result 0 | main.go:56:9:56:15 | call to test2 | main.go:56:2:56:15 | ... = ...[0] | | result 1 | main.go:54:10:54:15 | call to test | main.go:54:2:54:15 | ... := ...[1] | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/tst.go b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/tst.go new file mode 100644 index 00000000000..e36e3cf15d2 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/tst.go @@ -0,0 +1,11 @@ +package main + +import ( + "bytes" + "io" +) + +func test4(reader io.Reader) { + bytesBuffer := new(bytes.Buffer) + bytesBuffer.ReadFrom(reader) +}