mirror of
https://github.com/golang/go
synced 2024-11-24 08:50:14 -07:00
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 <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
d1731f8cbc
commit
0a48185b43
@ -19,6 +19,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
@ -1629,6 +1630,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) {
|
func BenchmarkCall(b *testing.B) {
|
||||||
fv := ValueOf(func(a, b string) {})
|
fv := ValueOf(func(a, b string) {})
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
@ -455,8 +455,14 @@ func (v Value) call(op string, in []Value) []Value {
|
|||||||
tv := t.Out(i)
|
tv := t.Out(i)
|
||||||
a := uintptr(tv.Align())
|
a := uintptr(tv.Align())
|
||||||
off = (off + a - 1) &^ (a - 1)
|
off = (off + a - 1) &^ (a - 1)
|
||||||
fl := flagIndir | flag(tv.Kind())
|
if tv.Size() != 0 {
|
||||||
ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(args) + off), fl}
|
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()
|
off += tv.Size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user