Skip to content

Commit

Permalink
[release-branch.go1.9] reflect: fix pointer past-the-end in Call with…
Browse files Browse the repository at this point in the history
… 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 <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-on: https://go-review.googlesource.com/70971
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
  • Loading branch information
cherrymui authored and rsc committed Oct 25, 2017
1 parent bd34e74 commit ff8289f
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 2 deletions.
25 changes: 25 additions & 0 deletions src/reflect/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"unicode"
Expand Down Expand Up @@ -1546,6 +1547,30 @@ func TestCallWithStruct(t *testing.T) {
}
}

func TestCallReturnsEmpty(t *testing.T) {
// Issue 21717: past-the-end pointer write in Call with
// nonzero-sized frame and zero-sized return value.
runtime.GC()
var finalized uint32
f := func() (emptyStruct, *int) {
i := new(int)
runtime.SetFinalizer(i, func(*int) { atomic.StoreUint32(&finalized, 1) })
return emptyStruct{}, i
}
v := ValueOf(f).Call(nil)[0] // out[0] should not alias out[1]'s memory, so the finalizer should run.
timeout := time.After(5 * time.Second)
for atomic.LoadUint32(&finalized) == 0 {
select {
case <-timeout:
t.Fatal("finalizer did not run")
default:
}
runtime.Gosched()
runtime.GC()
}
runtime.KeepAlive(v)
}

func BenchmarkCall(b *testing.B) {
fv := ValueOf(func(a, b string) {})
b.ReportAllocs()
Expand Down
10 changes: 8 additions & 2 deletions src/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,14 @@ func (v Value) call(op string, in []Value) []Value {
tv := t.Out(i)
a := uintptr(tv.Align())
off = (off + a - 1) &^ (a - 1)
fl := flagIndir | flag(tv.Kind())
ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(args) + off), fl}
if tv.Size() != 0 {
fl := flagIndir | flag(tv.Kind())
ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(args) + off), fl}
} else {
// For zero-sized return value, args+off may point to the next object.
// In this case, return the zero value instead.
ret[i] = Zero(tv)
}
off += tv.Size()
}
}
Expand Down

0 comments on commit ff8289f

Please sign in to comment.