2016-04-10 15:32:26 -06:00
|
|
|
// Copyright 2014 The Go Authors. All rights reserved.
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Issue 7978. Stack tracing didn't work during cgo code after calling a Go
|
|
|
|
// callback. Make sure GC works and the stack trace is correct.
|
|
|
|
|
|
|
|
package cgotest
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
void issue7978cb(void);
|
|
|
|
|
2014-12-25 23:20:29 -07:00
|
|
|
#if defined(__APPLE__) && defined(__arm__)
|
|
|
|
// on Darwin/ARM, libSystem doesn't provide implementation of the __sync_fetch_and_add
|
|
|
|
// primitive, and although gcc supports it, it doesn't inline its definition.
|
|
|
|
// Clang could inline its definition, so we require clang on Darwin/ARM.
|
|
|
|
#if defined(__clang__)
|
|
|
|
#define HAS_SYNC_FETCH_AND_ADD 1
|
|
|
|
#else
|
|
|
|
#define HAS_SYNC_FETCH_AND_ADD 0
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#define HAS_SYNC_FETCH_AND_ADD 1
|
|
|
|
#endif
|
|
|
|
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
// use ugly atomic variable sync since that doesn't require calling back into
|
|
|
|
// Go code or OS dependencies
|
|
|
|
static void issue7978c(uint32_t *sync) {
|
2014-12-25 23:20:29 -07:00
|
|
|
#if HAS_SYNC_FETCH_AND_ADD
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
while(__sync_fetch_and_add(sync, 0) != 0)
|
|
|
|
;
|
|
|
|
__sync_fetch_and_add(sync, 1);
|
|
|
|
while(__sync_fetch_and_add(sync, 0) != 2)
|
|
|
|
;
|
|
|
|
issue7978cb();
|
|
|
|
__sync_fetch_and_add(sync, 1);
|
|
|
|
while(__sync_fetch_and_add(sync, 0) != 6)
|
|
|
|
;
|
2014-12-25 23:20:29 -07:00
|
|
|
#endif
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
}
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"runtime"
|
2017-09-12 14:45:26 -06:00
|
|
|
"runtime/debug"
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
"strings"
|
|
|
|
"sync/atomic"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
var issue7978sync uint32
|
|
|
|
|
|
|
|
func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) {
|
|
|
|
runtime.GC()
|
|
|
|
buf := make([]byte, 65536)
|
|
|
|
trace := string(buf[:runtime.Stack(buf, true)])
|
|
|
|
for _, goroutine := range strings.Split(trace, "\n\n") {
|
|
|
|
if strings.Contains(goroutine, "test.issue7978go") {
|
|
|
|
trace := strings.Split(goroutine, "\n")
|
|
|
|
// look for the expected function in the stack
|
|
|
|
for i := 0; i < depth; i++ {
|
|
|
|
if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) {
|
|
|
|
t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if strings.Contains(trace[1+2*i], wantFunc) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace)
|
|
|
|
}
|
|
|
|
|
|
|
|
func issue7978wait(store uint32, wait uint32) {
|
|
|
|
if store != 0 {
|
|
|
|
atomic.StoreUint32(&issue7978sync, store)
|
|
|
|
}
|
|
|
|
for atomic.LoadUint32(&issue7978sync) != wait {
|
|
|
|
runtime.Gosched()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//export issue7978cb
|
|
|
|
func issue7978cb() {
|
runtime: fix undead arguments in cgocall
From the garbage collector's perspective, time can move backwards in
cgocall. However, in the midst of this time warp, the pointer
arguments to cgocall can go from dead back to live. If a stack growth
happens while they're dead and then a GC happens when they become live
again, GC can crash with a bad heap pointer.
Specifically, the sequence that leads to a panic is:
1. cgocall calls entersyscall, which saves the PC and SP of its call
site in cgocall. Call this PC/SP "X". At "X" both pointer arguments
are live.
2. cgocall calls asmcgocall. Call the PC/SP of this call "Y". At "Y"
neither pointer argument is live.
3. asmcgocall calls the C code, which eventually calls back into the
Go code.
4. cgocallbackg remembers the saved PC/SP "X" in some local variables,
calls exitsyscall, and then calls cgocallbackg1.
5. The Go code causes a stack growth. This stack unwind sees PC/SP "Y"
in the cgocall frame. Since the arguments are dead at "Y", they are
not adjusted.
6. The Go code returns to cgocallbackg1, which calls reentersyscall
with the recorded saved PC/SP "X", so "X" gets stashed back into
gp.syscallpc/sp.
7. GC scans the stack. It sees there's a saved syscall PC/SP, so it
starts the traceback at PC/SP "X". At "X" the arguments are considered
live, so it scans them, but since they weren't adjusted, the pointers
are bad, so it panics.
This issue started as of commit ca4089ad, when the compiler stopped
marking arguments as live for the whole function.
Since this is a variable liveness issue, fix it by adding KeepAlive
calls that keep the arguments live across this whole time warp.
The existing issue7978 test has all of the infrastructure for testing
this except that it's currently up to chance whether a stack growth
happens in the callback (it currently only happens on the
linux-amd64-noopt builder, for example). Update this test to force a
stack growth, which causes it to fail reliably without this fix.
Fixes #17785.
Change-Id: If706963819ee7814e6705693247bcb97a6f7adb8
Reviewed-on: https://go-review.googlesource.com/33710
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-11-30 08:50:40 -07:00
|
|
|
// Force a stack growth from the callback to put extra
|
|
|
|
// pressure on the runtime. See issue #17785.
|
|
|
|
growStack(64)
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
issue7978wait(3, 4)
|
|
|
|
}
|
|
|
|
|
runtime: fix undead arguments in cgocall
From the garbage collector's perspective, time can move backwards in
cgocall. However, in the midst of this time warp, the pointer
arguments to cgocall can go from dead back to live. If a stack growth
happens while they're dead and then a GC happens when they become live
again, GC can crash with a bad heap pointer.
Specifically, the sequence that leads to a panic is:
1. cgocall calls entersyscall, which saves the PC and SP of its call
site in cgocall. Call this PC/SP "X". At "X" both pointer arguments
are live.
2. cgocall calls asmcgocall. Call the PC/SP of this call "Y". At "Y"
neither pointer argument is live.
3. asmcgocall calls the C code, which eventually calls back into the
Go code.
4. cgocallbackg remembers the saved PC/SP "X" in some local variables,
calls exitsyscall, and then calls cgocallbackg1.
5. The Go code causes a stack growth. This stack unwind sees PC/SP "Y"
in the cgocall frame. Since the arguments are dead at "Y", they are
not adjusted.
6. The Go code returns to cgocallbackg1, which calls reentersyscall
with the recorded saved PC/SP "X", so "X" gets stashed back into
gp.syscallpc/sp.
7. GC scans the stack. It sees there's a saved syscall PC/SP, so it
starts the traceback at PC/SP "X". At "X" the arguments are considered
live, so it scans them, but since they weren't adjusted, the pointers
are bad, so it panics.
This issue started as of commit ca4089ad, when the compiler stopped
marking arguments as live for the whole function.
Since this is a variable liveness issue, fix it by adding KeepAlive
calls that keep the arguments live across this whole time warp.
The existing issue7978 test has all of the infrastructure for testing
this except that it's currently up to chance whether a stack growth
happens in the callback (it currently only happens on the
linux-amd64-noopt builder, for example). Update this test to force a
stack growth, which causes it to fail reliably without this fix.
Fixes #17785.
Change-Id: If706963819ee7814e6705693247bcb97a6f7adb8
Reviewed-on: https://go-review.googlesource.com/33710
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-11-30 08:50:40 -07:00
|
|
|
func growStack(n int) int {
|
|
|
|
var buf [128]int
|
|
|
|
if n == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return buf[growStack(n-1)]
|
|
|
|
}
|
|
|
|
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
func issue7978go() {
|
|
|
|
C.issue7978c((*C.uint32_t)(&issue7978sync))
|
|
|
|
issue7978wait(7, 8)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test7978(t *testing.T) {
|
2014-12-19 19:17:02 -07:00
|
|
|
if runtime.Compiler == "gccgo" {
|
|
|
|
t.Skip("gccgo can not do stack traces of C code")
|
|
|
|
}
|
2014-12-25 23:20:29 -07:00
|
|
|
if C.HAS_SYNC_FETCH_AND_ADD == 0 {
|
|
|
|
t.Skip("clang required for __sync_fetch_and_add support on darwin/arm")
|
|
|
|
}
|
2017-09-12 14:45:26 -06:00
|
|
|
debug.SetTraceback("2")
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
issue7978sync = 0
|
|
|
|
go issue7978go()
|
|
|
|
// test in c code, before callback
|
|
|
|
issue7978wait(0, 1)
|
2016-01-06 19:16:01 -07:00
|
|
|
issue7978check(t, "_Cfunc_issue7978c(", "", 1)
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
// test in go code, during callback
|
|
|
|
issue7978wait(2, 3)
|
|
|
|
issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3)
|
|
|
|
// test in c code, after callback
|
|
|
|
issue7978wait(4, 5)
|
2016-01-06 19:16:01 -07:00
|
|
|
issue7978check(t, "_Cfunc_issue7978c(", "_cgoexpwrap", 1)
|
runtime: keep g->syscallsp consistent after cgo->Go callbacks
Normally, the caller to runtime.entersyscall() must not return before
calling runtime.exitsyscall(), lest g->syscallsp become a dangling
pointer. runtime.cgocallbackg() violates this constraint. To work around
this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then
restore them after calling runtime.entersyscall(), which restores the
syscall stack frame pointer saved by cgocall. This allows the GC to
correctly trace a goroutine that is currently returning from a
Go->cgo->Go chain.
This also adds a check to proc.c that panics if g->syscallsp is clearly
invalid. It is not 100% foolproof, as it will not catch a case where the
stack was popped then pushed back beyond g->syscallsp, but it does catch
the present cgo issue and makes existing tests fail without the bugfix.
Fixes #7978.
LGTM=dvyukov, rsc
R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc
CC=golang-codereviews, rsc
https://golang.org/cl/131910043
2014-09-24 11:20:25 -06:00
|
|
|
// test in go code, after return from cgo
|
|
|
|
issue7978wait(6, 7)
|
|
|
|
issue7978check(t, "test.issue7978go(", "", 3)
|
|
|
|
atomic.StoreUint32(&issue7978sync, 8)
|
|
|
|
}
|