Skip to content

Commit 4f8645b

Browse files
authored
Merge pull request github#18235 from owen-mc/go/varargs-out-param
Go: Improve data flow out of variadic parameter
2 parents 22aaf74 + 7e5e634 commit 4f8645b

File tree

26 files changed

+350
-9
lines changed

26 files changed

+350
-9
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Data flow out of variadic parameters now works in more situations. Summary models defined using models-as-data work. Source models defined using models-as-data do not work yet.

go/ql/lib/semmle/go/dataflow/internal/ContainerFlow.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ predicate containerReadStep(Node node1, Node node2, Content c) {
6666
(
6767
node2.(Read).readsElement(node1, _)
6868
or
69+
exists(ImplicitVarargsSlice ivs |
70+
node1.(PostUpdateNode).getPreUpdateNode() = ivs and
71+
node2.(PostUpdateNode).getPreUpdateNode() = ivs.getCallNode().getAnImplicitVarargsArgument()
72+
)
73+
or
6974
node2.(RangeElementNode).getBase() = node1
7075
or
7176
// To model data flow from array elements of the base of a `SliceNode` to

go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,9 @@ module Public {
845845
or
846846
preupd = getAWrittenNode()
847847
or
848+
preupd instanceof ImplicitVarargsSlice and
849+
mutableType(preupd.(ImplicitVarargsSlice).getType().(SliceType).getElementType())
850+
or
848851
preupd = any(ArgumentNode arg).getACorrespondingSyntacticArgument() and
849852
mutableType(preupd.getType())
850853
) and

go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/completetest.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
99
import TestUtilities.InlineFlowTest
1010

1111
module Config implements DataFlow::ConfigSig {
12-
predicate isSource(DataFlow::Node src) { sourceNode(src, "qltest") }
12+
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
1313

14-
predicate isSink(DataFlow::Node src) { sinkNode(src, "qltest") }
14+
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
1515
}
1616

1717
import ValueFlowTest<Config>

go/ql/test/library-tests/semmle/go/dataflow/VarArgs/main.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ func source() string {
44
return "untrusted data"
55
}
66

7-
func sink(string) {
7+
func sink(any) {
88
}
99

1010
type A struct {
@@ -19,6 +19,10 @@ func functionWithVarArgsParameter(s ...string) string {
1919
return s[1]
2020
}
2121

22+
func functionWithVarArgsOutParameter(in string, out ...*string) {
23+
*out[0] = in
24+
}
25+
2226
func functionWithSliceOfStructsParameter(s []A) string {
2327
return s[1].f
2428
}
@@ -38,6 +42,12 @@ func main() {
3842
sink(functionWithVarArgsParameter(sSlice...)) // $ hasValueFlow="call to functionWithVarArgsParameter"
3943
sink(functionWithVarArgsParameter(s0, s1)) // $ hasValueFlow="call to functionWithVarArgsParameter"
4044

45+
var out1 *string
46+
var out2 *string
47+
functionWithVarArgsOutParameter(source(), out1, out2)
48+
sink(out1) // $ MISSING: hasValueFlow="out1"
49+
sink(out2) // $ MISSING: hasValueFlow="out2"
50+
4151
sliceOfStructs := []A{{f: source()}}
4252
sink(sliceOfStructs[0].f) // $ hasValueFlow="selection of f"
4353

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
testFailures
2+
invalidModelRow
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/go-all
4+
extensible: summaryModel
5+
data:
6+
- ["github.com/nonexistent/test", "", False, "FunctionWithParameter", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
7+
- ["github.com/nonexistent/test", "", False, "FunctionWithSliceParameter", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
8+
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsParameter", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
9+
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsOutParameter", "", "", "Argument[0]", "Argument[1].ArrayElement", "value", "manual"]
10+
- ["github.com/nonexistent/test", "", False, "FunctionWithSliceOfStructsParameter", "", "", "Argument[0].ArrayElement.Field[github.com/nonexistent/test.A.Field]", "ReturnValue", "value", "manual"]
11+
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsOfStructsParameter", "", "", "Argument[0].ArrayElement.Field[github.com/nonexistent/test.A.Field]", "ReturnValue", "value", "manual"]
12+
- addsTo:
13+
pack: codeql/go-all
14+
extensible: sourceModel
15+
data:
16+
- ["github.com/nonexistent/test", "", False, "VariadicSource", "", "", "Argument[0]", "qltest", "manual"]
17+
- addsTo:
18+
pack: codeql/go-all
19+
extensible: sinkModel
20+
data:
21+
- ["github.com/nonexistent/test", "", False, "VariadicSink", "", "", "Argument[0]", "qltest", "manual"]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import go
2+
import semmle.go.dataflow.ExternalFlow
3+
import ModelValidation
4+
import TestUtilities.InlineFlowTest
5+
6+
module Config implements DataFlow::ConfigSig {
7+
predicate isSource(DataFlow::Node source) {
8+
sourceNode(source, "qltest")
9+
or
10+
exists(Function fn | fn.hasQualifiedName(_, ["source", "taint"]) |
11+
source = fn.getACall().getResult()
12+
)
13+
}
14+
15+
predicate isSink(DataFlow::Node sink) {
16+
sinkNode(sink, "qltest")
17+
or
18+
exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument())
19+
}
20+
}
21+
22+
import FlowTest<Config, Config>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module semmle.go.Packages
2+
3+
go 1.23
4+
5+
require github.com/nonexistent/test v0.0.0-20200203000000-0000000000000
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"github.com/nonexistent/test"
5+
)
6+
7+
func source() string {
8+
return "untrusted data"
9+
}
10+
11+
func sink(any) {
12+
}
13+
14+
func main() {
15+
s := source()
16+
sink(test.FunctionWithParameter(s)) // $ hasValueFlow="call to FunctionWithParameter"
17+
18+
stringSlice := []string{source()}
19+
sink(stringSlice[0]) // $ hasValueFlow="index expression"
20+
21+
s0 := ""
22+
s1 := source()
23+
sSlice := []string{s0, s1}
24+
sink(test.FunctionWithParameter(sSlice[1])) // $ hasValueFlow="call to FunctionWithParameter"
25+
sink(test.FunctionWithSliceParameter(sSlice)) // $ hasValueFlow="call to FunctionWithSliceParameter"
26+
sink(test.FunctionWithVarArgsParameter(sSlice...)) // $ hasValueFlow="call to FunctionWithVarArgsParameter"
27+
sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ hasValueFlow="call to FunctionWithVarArgsParameter"
28+
29+
var out1 *string
30+
var out2 *string
31+
test.FunctionWithVarArgsOutParameter(source(), out1, out2)
32+
sink(out1) // $ hasValueFlow="out1"
33+
sink(out2) // $ hasValueFlow="out2"
34+
35+
sliceOfStructs := []test.A{{Field: source()}}
36+
sink(sliceOfStructs[0].Field) // $ hasValueFlow="selection of Field"
37+
38+
a0 := test.A{Field: ""}
39+
a1 := test.A{Field: source()}
40+
aSlice := []test.A{a0, a1}
41+
sink(test.FunctionWithSliceOfStructsParameter(aSlice)) // $ hasValueFlow="call to FunctionWithSliceOfStructsParameter"
42+
sink(test.FunctionWithVarArgsOfStructsParameter(aSlice...)) // $ hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
43+
sink(test.FunctionWithVarArgsOfStructsParameter(a0, a1)) // $ hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
44+
45+
var variadicSource string
46+
test.VariadicSource(&variadicSource)
47+
sink(variadicSource) // $ MISSING: hasTaintFlow="variadicSource"
48+
49+
test.VariadicSink(source()) // $ hasTaintFlow="[]type{args}"
50+
51+
}

0 commit comments

Comments
 (0)