panic("recurseFn panic")
}
}
+
+// Try to reproduce issue #37688, where a pointer to an open-coded defer struct is
+// mistakenly held, and that struct keeps a pointer to a stack-allocated defer
+// struct, and that stack-allocated struct gets overwritten or the stack gets
+// moved, so a memory error happens on GC.
+func TestIssue37688(t *testing.T) {
+ for j := 0; j < 10; j++ {
+ g2()
+ g3()
+ }
+}
+
+type foo struct {
+}
+
+func (f *foo) method1() {
+ fmt.Fprintln(os.Stderr, "method1")
+}
+
+func (f *foo) method2() {
+ fmt.Fprintln(os.Stderr, "method2")
+}
+
+func g2() {
+ var a foo
+ ap := &a
+ // The loop forces this defer to be heap-allocated and the remaining two
+ // to be stack-allocated.
+ for i := 0; i < 1; i++ {
+ defer ap.method1()
+ }
+ defer ap.method2()
+ defer ap.method1()
+ ff1(ap, 1, 2, 3, 4, 5, 6, 7, 8, 9)
+ // Try to get the stack to be be moved by growing it too large, so
+ // existing stack-allocated defer becomes invalid.
+ rec1(2000)
+}
+
+func g3() {
+ // Mix up the stack layout by adding in an extra function frame
+ g2()
+}
+
+func ff1(ap *foo, a, b, c, d, e, f, g, h, i int) {
+ defer ap.method1()
+
+ // Make a defer that has a very large set of args, hence big size for the
+ // defer record for the open-coded frame (which means it won't use the
+ // defer pool)
+ defer func(ap *foo, a, b, c, d, e, f, g, h, i int) {
+ if v := recover(); v != nil {
+ fmt.Fprintln(os.Stderr, "did recover")
+ }
+ fmt.Fprintln(os.Stderr, "debug", ap, a, b, c, d, e, f, g, h)
+ }(ap, a, b, c, d, e, f, g, h, i)
+ panic("ff1 panic")
+}
+
+func rec1(max int) {
+ if max > 0 {
+ rec1(max - 1)
+ } else {
+ fmt.Fprintln(os.Stderr, "finished recursion", max)
+ }
+}
// The compiler turns a defer statement into a call to this.
//go:nosplit
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
- if getg().m.curg != getg() {
+ gp := getg()
+ if gp.m.curg != gp {
// go code on the system stack can't defer
throw("defer on system stack")
}
if d._panic != nil {
throw("deferproc: d.panic != nil after newdefer")
}
+ d.link = gp._defer
+ gp._defer = d
d.fn = fn
d.pc = callerpc
d.sp = sp
}
// Allocate a Defer, usually using per-P pool.
-// Each defer must be released with freedefer.
+// Each defer must be released with freedefer. The defer is not
+// added to any defer chain yet.
//
// This must not grow the stack because there may be a frame without
// stack map information when this is called.
}
d.siz = siz
d.heap = true
- d.link = gp._defer
- gp._defer = d
return d
}