Skip to content

Commit 0aa7f8f

Browse files
randall77dmitshur
authored andcommitted
[release-branch.go1.17] reflect: keep pointer in aggregate-typed args live in Call
When register ABI is used, reflect.Value.Call prepares the call arguments in a memory representation of the argument registers. It has special handling to keep the pointers in arguments live. Currently, this handles pointer-typed arguments. But when an argument is an aggregate-type that contains pointers and passed in registers, it currently doesn't keep the pointers live. Do so in this CL. Fixes #49961 Change-Id: I9264a8767e2a2c48573f6047144759b845dcf480 Reviewed-on: https://go-review.googlesource.com/c/go/+/369098 Trust: Keith Randall <[email protected]> Run-TryBot: Keith Randall <[email protected]> Reviewed-by: Cherry Mui <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent 4223f6e commit 0aa7f8f

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

src/internal/abi/abi.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@ type RegArgs struct {
3333
ReturnIsPtr IntArgRegBitmap
3434
}
3535

36+
func (r *RegArgs) Dump() {
37+
print("Ints:")
38+
for _, x := range r.Ints {
39+
print(" ", x)
40+
}
41+
println()
42+
print("Floats:")
43+
for _, x := range r.Floats {
44+
print(" ", x)
45+
}
46+
println()
47+
print("Ptrs:")
48+
for _, x := range r.Ptrs {
49+
print(" ", x)
50+
}
51+
println()
52+
}
53+
3654
// IntArgRegBitmap is a bitmap large enough to hold one bit per
3755
// integer argument/return register.
3856
type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8

src/reflect/all_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6270,6 +6270,29 @@ func TestCallMethodJump(t *testing.T) {
62706270
*CallGC = false
62716271
}
62726272

6273+
func TestCallArgLive(t *testing.T) {
6274+
type T struct{ X, Y *string } // pointerful aggregate
6275+
6276+
F := func(t T) { *t.X = "ok" }
6277+
6278+
// In reflect.Value.Call, trigger a garbage collection in reflect.call
6279+
// between marshaling argument and the actual call.
6280+
*CallGC = true
6281+
6282+
x := new(string)
6283+
runtime.SetFinalizer(x, func(p *string) {
6284+
if *p != "ok" {
6285+
t.Errorf("x dead prematurely")
6286+
}
6287+
})
6288+
v := T{x, nil}
6289+
6290+
ValueOf(F).Call([]Value{ValueOf(v)})
6291+
6292+
// Stop garbage collecting during reflect.call.
6293+
*CallGC = false
6294+
}
6295+
62736296
func TestMakeFuncStackCopy(t *testing.T) {
62746297
target := func(in []Value) []Value {
62756298
runtime.GC()

src/reflect/value.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ func (v Value) CallSlice(in []Value) []Value {
352352
return v.call("CallSlice", in)
353353
}
354354

355-
var callGC bool // for testing; see TestCallMethodJump
355+
var callGC bool // for testing; see TestCallMethodJump and TestCallArgLive
356356

357357
const debugReflectCall = false
358358

@@ -509,12 +509,16 @@ func (v Value) call(op string, in []Value) []Value {
509509
// Copy values to "integer registers."
510510
if v.flag&flagIndir != 0 {
511511
offset := add(v.ptr, st.offset, "precomputed value offset")
512-
memmove(unsafe.Pointer(&regArgs.Ints[st.ireg]), offset, st.size)
513-
} else {
514512
if st.kind == abiStepPointer {
515513
// Duplicate this pointer in the pointer area of the
516514
// register space. Otherwise, there's the potential for
517515
// this to be the last reference to v.ptr.
516+
regArgs.Ptrs[st.ireg] = *(*unsafe.Pointer)(offset)
517+
}
518+
memmove(unsafe.Pointer(&regArgs.Ints[st.ireg]), offset, st.size)
519+
} else {
520+
if st.kind == abiStepPointer {
521+
// See the comment in abiStepPointer case above.
518522
regArgs.Ptrs[st.ireg] = v.ptr
519523
}
520524
regArgs.Ints[st.ireg] = uintptr(v.ptr)
@@ -539,6 +543,15 @@ func (v Value) call(op string, in []Value) []Value {
539543
// Mark pointers in registers for the return path.
540544
regArgs.ReturnIsPtr = abi.outRegPtrs
541545

546+
if debugReflectCall {
547+
regArgs.Dump()
548+
}
549+
550+
// For testing; see TestCallArgLive.
551+
if callGC {
552+
runtime.GC()
553+
}
554+
542555
// Call.
543556
call(frametype, fn, stackArgs, uint32(frametype.size), uint32(abi.retOffset), uint32(frameSize), &regArgs)
544557

0 commit comments

Comments
 (0)