From a73d68e75eece80f8514bb1b368420843c1f58ad Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 20 Oct 2016 22:27:39 -0400 Subject: [PATCH] runtime: fix call* signatures and deferArgs with siz=0 This commit fixes two bizarrely related bugs: 1. The signatures for the call* functions were wrong, indicating that they had only two pointer arguments instead of three. We didn't notice because the call* functions are defined by a macro expansion, which go vet doesn't see. 2. deferArgs on a defer object with a zero-sized frame returned a pointer just past the end of the allocated object, which is illegal in Go (and can cause the "sweep increased allocation count" crashes). In a fascinating twist, these two bugs canceled each other out, which is why I'm fixing them together. The pointer returned by deferArgs is used in only two ways: as an argument to memmove and as an argument to reflectcall. memmove is NOSPLIT, so the argument was unobservable. reflectcall immediately tail calls one of the call* functions, which are not NOSPLIT, but the deferArgs pointer just happened to be the third argument that was accidentally marked as a scalar. Hence, when the garbage collector scanned the stack, it didn't see the bad pointer as a pointer. I believe this was all ultimately benign. In principle, stack growth during the reflectcall could fail to update the args pointer, but it never points to the stack, so it never needs to be updated. Also in principle, the garbage collector could fail to mark the args object because of the incorrect call* signatures, but in all calls to reflectcall (including the ones spelled "call" in the reflect package) the args object is kept live by the calling stack. Change-Id: Ic932c79d5f4382be23118fdd9dba9688e9169e28 Reviewed-on: https://go-review.googlesource.com/31654 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/runtime/panic.go | 4 ++++ src/runtime/stubs.go | 52 ++++++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/runtime/panic.go b/src/runtime/panic.go index f78e67f9bb2084..73924365c34da7 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -168,6 +168,10 @@ func testdefersizes() { // immediately after the _defer header in memory. //go:nosplit func deferArgs(d *_defer) unsafe.Pointer { + if d.siz == 0 { + // Avoid pointer past the defer allocation. + return nil + } return add(unsafe.Pointer(d), unsafe.Sizeof(*d)) } diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 88f4139ba33651..b73a97f73567fd 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -233,32 +233,32 @@ func time_now() (sec int64, nsec int32) // in asm_*.s // not called directly; definitions here supply type information for traceback. -func call32(fn, arg unsafe.Pointer, n, retoffset uint32) -func call64(fn, arg unsafe.Pointer, n, retoffset uint32) -func call128(fn, arg unsafe.Pointer, n, retoffset uint32) -func call256(fn, arg unsafe.Pointer, n, retoffset uint32) -func call512(fn, arg unsafe.Pointer, n, retoffset uint32) -func call1024(fn, arg unsafe.Pointer, n, retoffset uint32) -func call2048(fn, arg unsafe.Pointer, n, retoffset uint32) -func call4096(fn, arg unsafe.Pointer, n, retoffset uint32) -func call8192(fn, arg unsafe.Pointer, n, retoffset uint32) -func call16384(fn, arg unsafe.Pointer, n, retoffset uint32) -func call32768(fn, arg unsafe.Pointer, n, retoffset uint32) -func call65536(fn, arg unsafe.Pointer, n, retoffset uint32) -func call131072(fn, arg unsafe.Pointer, n, retoffset uint32) -func call262144(fn, arg unsafe.Pointer, n, retoffset uint32) -func call524288(fn, arg unsafe.Pointer, n, retoffset uint32) -func call1048576(fn, arg unsafe.Pointer, n, retoffset uint32) -func call2097152(fn, arg unsafe.Pointer, n, retoffset uint32) -func call4194304(fn, arg unsafe.Pointer, n, retoffset uint32) -func call8388608(fn, arg unsafe.Pointer, n, retoffset uint32) -func call16777216(fn, arg unsafe.Pointer, n, retoffset uint32) -func call33554432(fn, arg unsafe.Pointer, n, retoffset uint32) -func call67108864(fn, arg unsafe.Pointer, n, retoffset uint32) -func call134217728(fn, arg unsafe.Pointer, n, retoffset uint32) -func call268435456(fn, arg unsafe.Pointer, n, retoffset uint32) -func call536870912(fn, arg unsafe.Pointer, n, retoffset uint32) -func call1073741824(fn, arg unsafe.Pointer, n, retoffset uint32) +func call32(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call64(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call128(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call256(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call512(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call1024(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call2048(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call4096(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call8192(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call16384(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call32768(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call65536(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call131072(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call262144(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call524288(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call1048576(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call2097152(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call4194304(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call8388608(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call16777216(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call33554432(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call67108864(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call134217728(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call268435456(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call536870912(typ, fn, arg unsafe.Pointer, n, retoffset uint32) +func call1073741824(typ, fn, arg unsafe.Pointer, n, retoffset uint32) func systemstack_switch()