Skip to content

Commit 97b3f00

Browse files
author
ian
committed
runtime: in getTraceback, set gp->m before gogo
Currently, when collecting a traceback for another goroutine, getTraceback calls gogo(gp) switching to gp, which will resume in mcall, which will call gtraceback, which will set up gp->m. There is a gap between setting the current running g to gp and setting gp->m. If a profiling signal arrives in between, sigtramp will see a non-nil gp with a nil m, and will seg fault. Fix this by setting up gp->m first. Fixes golang/go#29448. Reviewed-on: https://go-review.googlesource.com/c/156038 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@267658 138bc75d-0d04-0410-961f-82ee72b054a4
1 parent 5f34f21 commit 97b3f00

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

gcc/go/gofrontend/MERGE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
2ce291eaee427799bfcde256929dab89e0ab61eb
1+
c257303eaef143663216e483857d5b259e05753f
22

33
The first line of this file holds the git revision number of the last
44
merge done from the gofrontend repository.

libgo/go/runtime/pprof/pprof_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,3 +946,38 @@ func TestAtomicLoadStore64(t *testing.T) {
946946
atomic.StoreUint64(&flag, 1)
947947
<-done
948948
}
949+
950+
func TestTracebackAll(t *testing.T) {
951+
// With gccgo, if a profiling signal arrives at the wrong time
952+
// during traceback, it may crash or hang. See issue #29448.
953+
f, err := ioutil.TempFile("", "proftraceback")
954+
if err != nil {
955+
t.Fatalf("TempFile: %v", err)
956+
}
957+
defer os.Remove(f.Name())
958+
defer f.Close()
959+
960+
if err := StartCPUProfile(f); err != nil {
961+
t.Fatal(err)
962+
}
963+
defer StopCPUProfile()
964+
965+
ch := make(chan int)
966+
defer close(ch)
967+
968+
count := 10
969+
for i := 0; i < count; i++ {
970+
go func() {
971+
<-ch // block
972+
}()
973+
}
974+
975+
N := 10000
976+
if testing.Short() {
977+
N = 500
978+
}
979+
buf := make([]byte, 10*1024)
980+
for i := 0; i < N; i++ {
981+
runtime.Stack(buf, true)
982+
}
983+
}

libgo/runtime/proc.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,11 @@ void getTraceback(G*, G*) __asm__(GOSYM_PREFIX "runtime.getTraceback");
442442
// goroutine stored in the traceback field, which is me.
443443
void getTraceback(G* me, G* gp)
444444
{
445+
M* holdm;
446+
447+
holdm = gp->m;
448+
gp->m = me->m;
449+
445450
#ifdef USING_SPLIT_STACK
446451
__splitstack_getcontext((void*)(&me->stackcontext[0]));
447452
#endif
@@ -450,6 +455,8 @@ void getTraceback(G* me, G* gp)
450455
if (gp->traceback != 0) {
451456
runtime_gogo(gp);
452457
}
458+
459+
gp->m = holdm;
453460
}
454461

455462
// Do a stack trace of gp, and then restore the context to
@@ -459,17 +466,11 @@ void
459466
gtraceback(G* gp)
460467
{
461468
Traceback* traceback;
462-
M* holdm;
463469

464470
traceback = (Traceback*)gp->traceback;
465471
gp->traceback = 0;
466-
holdm = gp->m;
467-
if(holdm != nil && holdm != g->m)
468-
runtime_throw("gtraceback: m is not nil");
469-
gp->m = traceback->gp->m;
470472
traceback->c = runtime_callers(1, traceback->locbuf,
471473
sizeof traceback->locbuf / sizeof traceback->locbuf[0], false);
472-
gp->m = holdm;
473474
runtime_gogo(traceback->gp);
474475
}
475476

0 commit comments

Comments
 (0)