Skip to content

Commit ff8289f

Browse files
cherrymuirsc
authored andcommitted
[release-branch.go1.9] reflect: fix pointer past-the-end in Call with zero-sized return value
If a function with nonzero frame but zero-sized return value is Call'd, we may write a past-the-end pointer in preparing the return Values. Fix by return the zero value for zero-sized return value. Fixes #21717. Change-Id: I5351cd86d898467170a888b4c3fc9392f0e7aa3b Reviewed-on: https://go-review.googlesource.com/60811 Run-TryBot: Cherry Zhang <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Austin Clements <[email protected]> Reviewed-on: https://go-review.googlesource.com/70971 Run-TryBot: Russ Cox <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent bd34e74 commit ff8289f

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

src/reflect/all_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"strconv"
2020
"strings"
2121
"sync"
22+
"sync/atomic"
2223
"testing"
2324
"time"
2425
"unicode"
@@ -1546,6 +1547,30 @@ func TestCallWithStruct(t *testing.T) {
15461547
}
15471548
}
15481549

1550+
func TestCallReturnsEmpty(t *testing.T) {
1551+
// Issue 21717: past-the-end pointer write in Call with
1552+
// nonzero-sized frame and zero-sized return value.
1553+
runtime.GC()
1554+
var finalized uint32
1555+
f := func() (emptyStruct, *int) {
1556+
i := new(int)
1557+
runtime.SetFinalizer(i, func(*int) { atomic.StoreUint32(&finalized, 1) })
1558+
return emptyStruct{}, i
1559+
}
1560+
v := ValueOf(f).Call(nil)[0] // out[0] should not alias out[1]'s memory, so the finalizer should run.
1561+
timeout := time.After(5 * time.Second)
1562+
for atomic.LoadUint32(&finalized) == 0 {
1563+
select {
1564+
case <-timeout:
1565+
t.Fatal("finalizer did not run")
1566+
default:
1567+
}
1568+
runtime.Gosched()
1569+
runtime.GC()
1570+
}
1571+
runtime.KeepAlive(v)
1572+
}
1573+
15491574
func BenchmarkCall(b *testing.B) {
15501575
fv := ValueOf(func(a, b string) {})
15511576
b.ReportAllocs()

src/reflect/value.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -456,8 +456,14 @@ func (v Value) call(op string, in []Value) []Value {
456456
tv := t.Out(i)
457457
a := uintptr(tv.Align())
458458
off = (off + a - 1) &^ (a - 1)
459-
fl := flagIndir | flag(tv.Kind())
460-
ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(args) + off), fl}
459+
if tv.Size() != 0 {
460+
fl := flagIndir | flag(tv.Kind())
461+
ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(args) + off), fl}
462+
} else {
463+
// For zero-sized return value, args+off may point to the next object.
464+
// In this case, return the zero value instead.
465+
ret[i] = Zero(tv)
466+
}
461467
off += tv.Size()
462468
}
463469
}

0 commit comments

Comments
 (0)