Skip to content

Commit d5bd797

Browse files
committed
runtime: fix getArgInfo for deferred reflection calls
getArgInfo for reflect.makeFuncStub and reflect.methodValueCall is necessarily special. These have dynamically determined argument maps that are stored in their context (that is, their *funcval). These functions are written to store this context at 0(SP) when called, and getArgInfo retrieves it from there. This technique works if getArgInfo is passed an active call frame for one of these functions. However, getArgInfo is also used in tracebackdefers, where the "call" is not a true call with an active stack frame, but a deferred call. In this situation, getArgInfo currently crashes because tracebackdefers passes a frame with sp set to 0. However, the entire approach used by getArgInfo is flawed in this situation because the wrapper has not actually executed, and hence hasn't saved this metadata to any stack frame. In the defer case, we know the *funcval from the _defer itself, so we can fix this by teaching getArgInfo to use the *funcval context directly when its available, and otherwise get it from the active call frame. While we're here, this commit simplifies getArgInfo a bit by making it play more nicely with the type system. Rather than decoding the *reflect.methodValue that is the wrapper's context as a *[2]uintptr, just write out a copy of the reflect.methodValue type in the runtime. Fixes #16331. Fixes #17471. Change-Id: I81db4d985179b4a81c68c490cceeccbfc675456a Reviewed-on: https://go-review.googlesource.com/31138 Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 687d9d5 commit d5bd797

File tree

3 files changed

+86
-7
lines changed

3 files changed

+86
-7
lines changed

src/reflect/makefunc.go

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
7070
// word in the passed-in argument frame.
7171
func makeFuncStub()
7272

73+
// This type is partially duplicated as runtime.reflectMethodValue.
74+
// Any changes should be reflected in both.
7375
type methodValue struct {
7476
fn uintptr
7577
stack *bitVector // stack bitmap for args - offset known to runtime

src/runtime/traceback.go

+36-7
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v uns
107107
}
108108
frame.fn = f
109109
frame.argp = uintptr(deferArgs(d))
110-
frame.arglen, frame.argmap = getArgInfo(&frame, f, true)
110+
frame.arglen, frame.argmap = getArgInfo(&frame, f, true, fn)
111111
}
112112
frame.continpc = frame.pc
113113
if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
@@ -339,7 +339,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
339339
// metadata recorded by f's caller.
340340
if callback != nil || printing {
341341
frame.argp = frame.fp + sys.MinFrameSize
342-
frame.arglen, frame.argmap = getArgInfo(&frame, f, callback != nil)
342+
frame.arglen, frame.argmap = getArgInfo(&frame, f, callback != nil, nil)
343343
}
344344

345345
// Determine frame's 'continuation PC', where it can continue.
@@ -546,19 +546,48 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
546546
return n
547547
}
548548

549-
func getArgInfo(frame *stkframe, f *_func, needArgMap bool) (arglen uintptr, argmap *bitvector) {
549+
// reflectMethodValue is a partial duplicate of reflect.methodValue.
550+
type reflectMethodValue struct {
551+
fn uintptr
552+
stack *bitvector // args bitmap
553+
}
554+
555+
// getArgInfo returns the argument frame information for a call to f
556+
// with call frame frame.
557+
//
558+
// This is used for both actual calls with active stack frames and for
559+
// deferred calls that are not yet executing. If this is an actual
560+
// call, ctxt must be nil (getArgInfo will retrieve what it needs from
561+
// the active stack frame). If this is a deferred call, ctxt must be
562+
// the function object that was deferred.
563+
func getArgInfo(frame *stkframe, f *_func, needArgMap bool, ctxt *funcval) (arglen uintptr, argmap *bitvector) {
550564
arglen = uintptr(f.args)
551565
if needArgMap && f.args == _ArgsSizeUnknown {
552566
// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
553567
switch funcname(f) {
554568
case "reflect.makeFuncStub", "reflect.methodValueCall":
555-
arg0 := frame.sp + sys.MinFrameSize
556-
fn := *(**[2]uintptr)(unsafe.Pointer(arg0))
557-
if fn[0] != f.entry {
569+
// These take a *reflect.methodValue as their
570+
// context register.
571+
var mv *reflectMethodValue
572+
if ctxt != nil {
573+
// This is not an actual call, but a
574+
// deferred call. The function value
575+
// is itself the *reflect.methodValue.
576+
mv = (*reflectMethodValue)(unsafe.Pointer(ctxt))
577+
} else {
578+
// This is a real call that took the
579+
// *reflect.methodValue as its context
580+
// register and immediately saved it
581+
// to 0(SP). Get the methodValue from
582+
// 0(SP).
583+
arg0 := frame.sp + sys.MinFrameSize
584+
mv = *(**reflectMethodValue)(unsafe.Pointer(arg0))
585+
}
586+
if mv.fn != f.entry {
558587
print("runtime: confused by ", funcname(f), "\n")
559588
throw("reflect mismatch")
560589
}
561-
bv := (*bitvector)(unsafe.Pointer(fn[1]))
590+
bv := mv.stack
562591
arglen = uintptr(bv.n * sys.PtrSize)
563592
argmap = bv
564593
}

test/fixedbugs/issue16331.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// run
2+
3+
// Copyright 2016 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Perform tracebackdefers with a deferred reflection method.
8+
9+
package main
10+
11+
import "reflect"
12+
13+
type T struct{}
14+
15+
func (T) M() {
16+
}
17+
18+
func F(args []reflect.Value) (results []reflect.Value) {
19+
return nil
20+
}
21+
22+
func main() {
23+
done := make(chan bool)
24+
go func() {
25+
// Test reflect.makeFuncStub.
26+
t := reflect.TypeOf((func())(nil))
27+
f := reflect.MakeFunc(t, F).Interface().(func())
28+
defer f()
29+
growstack(10000)
30+
done <- true
31+
}()
32+
<-done
33+
go func() {
34+
// Test reflect.methodValueCall.
35+
f := reflect.ValueOf(T{}).Method(0).Interface().(func())
36+
defer f()
37+
growstack(10000)
38+
done <- true
39+
}()
40+
<-done
41+
}
42+
43+
func growstack(x int) {
44+
if x == 0 {
45+
return
46+
}
47+
growstack(x - 1)
48+
}

0 commit comments

Comments
 (0)