mirror of
https://github.com/golang/go
synced 2024-11-18 16:04:44 -07:00
6effdd28de
Make sure the pointer to the heap copy of an output parameter is kept live throughout the function. The function could panic at any point, and then a defer could recover. Thus, we need the pointer to the heap copy always available so the post-deferreturn code can copy the return value back to the stack. Before this CL, the pointer to the heap copy could be considered dead in certain situations, like code which is reverse dominated by a panic call. Fixes #16095. Change-Id: Ic3800423e563670e5b567b473bf4c84cddb49a4c Reviewed-on: https://go-review.googlesource.com/24213 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com>
105 lines
1.9 KiB
Go
105 lines
1.9 KiB
Go
// run
|
|
|
|
// Copyright 2016 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.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
)
|
|
|
|
var sink *[20]byte
|
|
|
|
func f() (x [20]byte) {
|
|
// Initialize x.
|
|
for i := range x {
|
|
x[i] = byte(i)
|
|
}
|
|
|
|
// Force x to be allocated on the heap.
|
|
sink = &x
|
|
sink = nil
|
|
|
|
// Go to deferreturn after the panic below.
|
|
defer func() {
|
|
recover()
|
|
}()
|
|
|
|
// This call collects the heap-allocated version of x (oops!)
|
|
runtime.GC()
|
|
|
|
// Allocate that same object again and clobber it.
|
|
y := new([20]byte)
|
|
for i := 0; i < 20; i++ {
|
|
y[i] = 99
|
|
}
|
|
// Make sure y is heap allocated.
|
|
sink = y
|
|
|
|
panic(nil)
|
|
|
|
// After the recover we reach the deferreturn, which
|
|
// copies the heap version of x back to the stack.
|
|
// It gets the pointer to x from a stack slot that was
|
|
// not marked as live during the call to runtime.GC().
|
|
}
|
|
|
|
var sinkint int
|
|
|
|
func g(p *int) (x [20]byte) {
|
|
// Initialize x.
|
|
for i := range x {
|
|
x[i] = byte(i)
|
|
}
|
|
|
|
// Force x to be allocated on the heap.
|
|
sink = &x
|
|
sink = nil
|
|
|
|
// Go to deferreturn after the panic below.
|
|
defer func() {
|
|
recover()
|
|
}()
|
|
|
|
// This call collects the heap-allocated version of x (oops!)
|
|
runtime.GC()
|
|
|
|
// Allocate that same object again and clobber it.
|
|
y := new([20]byte)
|
|
for i := 0; i < 20; i++ {
|
|
y[i] = 99
|
|
}
|
|
// Make sure y is heap allocated.
|
|
sink = y
|
|
|
|
// panic with a non-call (with no fallthrough)
|
|
for {
|
|
sinkint = *p
|
|
}
|
|
|
|
// After the recover we reach the deferreturn, which
|
|
// copies the heap version of x back to the stack.
|
|
// It gets the pointer to x from a stack slot that was
|
|
// not marked as live during the call to runtime.GC().
|
|
}
|
|
|
|
func main() {
|
|
x := f()
|
|
for i, v := range x {
|
|
if v != byte(i) {
|
|
fmt.Printf("%v\n", x)
|
|
panic("bad f")
|
|
}
|
|
}
|
|
x = g(nil)
|
|
for i, v := range x {
|
|
if v != byte(i) {
|
|
fmt.Printf("%v\n", x)
|
|
panic("bad g")
|
|
}
|
|
}
|
|
}
|