mirror of
https://github.com/golang/go.git
synced 2025-11-10 21:51:05 +00:00
Move the zeroing of results earlier. In particular, they need to
come before any move-to-heap operations, as those require allocation.
Those allocations are points at which the GC can see the uninitialized
result slots.
For the function:
func f() (x, y, z *int) {
defer(){}()
escape(&y)
return
}
We used to generate code like this:
x = nil
y = nil
&y = new(int)
z = nil
Now we will generate:
x = nil
y = nil
z = nil
&y = new(int)
Since the fix for #18860, the return slots are always live if there
is a defer, so the former ordering allowed the GC to see junk
in the z slot.
Fixes #19078
Change-Id: I71554ae437549725bb79e13b2c100b2911d47ed4
Reviewed-on: https://go-review.googlesource.com/38133
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
42 lines
1.2 KiB
Go
42 lines
1.2 KiB
Go
// run
|
|
|
|
// Copyright 2017 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Issue 19078: liveness & zero-initialization of results
|
|
// when there is a defer.
|
|
package main
|
|
|
|
import "unsafe"
|
|
|
|
func main() {
|
|
// Construct an invalid pointer. We do this by
|
|
// making a pointer which points to the unused space
|
|
// between the last 48-byte object in a span and the
|
|
// end of the span (there are 32 unused bytes there).
|
|
p := new([48]byte) // make a 48-byte object
|
|
sink = &p // escape it, so it allocates for real
|
|
u := uintptr(unsafe.Pointer(p)) // get its address
|
|
u = u >> 13 << 13 // round down to page size
|
|
u += 1<<13 - 1 // add almost a page
|
|
|
|
for i := 0; i < 1000000; i++ {
|
|
_ = identity(u) // installs u at return slot
|
|
_ = liveReturnSlot(nil) // incorrectly marks return slot as live
|
|
}
|
|
}
|
|
|
|
//go:noinline
|
|
func liveReturnSlot(x *int) *int {
|
|
defer func() {}() // causes return slot to be marked live
|
|
sink = &x // causes x to be moved to the heap, triggering allocation
|
|
return x
|
|
}
|
|
|
|
//go:noinline
|
|
func identity(x uintptr) uintptr {
|
|
return x
|
|
}
|
|
|
|
var sink interface{}
|