Skip to content

Commit 0a48185

Browse files
committed
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]>
1 parent d1731f8 commit 0a48185

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

src/reflect/all_test.go

Lines changed: 25 additions & 0 deletions
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"
@@ -1629,6 +1630,30 @@ func TestCallWithStruct(t *testing.T) {
16291630
}
16301631
}
16311632

1633+
func TestCallReturnsEmpty(t *testing.T) {
1634+
// Issue 21717: past-the-end pointer write in Call with
1635+
// nonzero-sized frame and zero-sized return value.
1636+
runtime.GC()
1637+
var finalized uint32
1638+
f := func() (emptyStruct, *int) {
1639+
i := new(int)
1640+
runtime.SetFinalizer(i, func(*int) { atomic.StoreUint32(&finalized, 1) })
1641+
return emptyStruct{}, i
1642+
}
1643+
v := ValueOf(f).Call(nil)[0] // out[0] should not alias out[1]'s memory, so the finalizer should run.
1644+
timeout := time.After(5 * time.Second)
1645+
for atomic.LoadUint32(&finalized) == 0 {
1646+
select {
1647+
case <-timeout:
1648+
t.Fatal("finalizer did not run")
1649+
default:
1650+
}
1651+
runtime.Gosched()
1652+
runtime.GC()
1653+
}
1654+
runtime.KeepAlive(v)
1655+
}
1656+
16321657
func BenchmarkCall(b *testing.B) {
16331658
fv := ValueOf(func(a, b string) {})
16341659
b.ReportAllocs()

src/reflect/value.go

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

0 commit comments

Comments
 (0)