Skip to content

Commit 091db63

Browse files
committed
runtime: fix cgo signals detection
CL 64070 removed lockOSThread from the cgocall path, but didn't update the signal-in-cgo detection in sighandler. As a result, signals that arrive during a cgo call are treated like they arrived during Go execution, breaking the traceback. Update the cgo detection to fix the backtrace. Fixes #47522 Change-Id: I61d77ba6465f55e3e6187246d79675ba8467ec23 Reviewed-on: https://go-review.googlesource.com/c/go/+/339989 Trust: Michael Pratt <[email protected]> Run-TryBot: Michael Pratt <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent 3a0cd11 commit 091db63

File tree

5 files changed

+100
-4
lines changed

5 files changed

+100
-4
lines changed

src/runtime/crash_cgo_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,29 @@ func TestCgoTracebackSigpanic(t *testing.T) {
536536
}
537537
}
538538

539+
func TestCgoPanicCallback(t *testing.T) {
540+
t.Parallel()
541+
got := runTestProg(t, "testprogcgo", "PanicCallback")
542+
t.Log(got)
543+
want := "panic: runtime error: invalid memory address or nil pointer dereference"
544+
if !strings.Contains(got, want) {
545+
t.Errorf("did not see %q in output", want)
546+
}
547+
want = "panic_callback"
548+
if !strings.Contains(got, want) {
549+
t.Errorf("did not see %q in output", want)
550+
}
551+
want = "PanicCallback"
552+
if !strings.Contains(got, want) {
553+
t.Errorf("did not see %q in output", want)
554+
}
555+
// No runtime errors like "runtime: unexpected return pc".
556+
nowant := "runtime: "
557+
if strings.Contains(got, nowant) {
558+
t.Errorf("did not see %q in output", want)
559+
}
560+
}
561+
539562
// Test that C code called via cgo can use large Windows thread stacks
540563
// and call back in to Go without crashing. See issue #20975.
541564
//
@@ -603,6 +626,28 @@ func TestSegv(t *testing.T) {
603626
}
604627
}
605628

629+
func TestAbortInCgo(t *testing.T) {
630+
switch runtime.GOOS {
631+
case "plan9", "windows":
632+
// N.B. On Windows, C abort() causes the program to exit
633+
// without going through the runtime at all.
634+
t.Skipf("no signals on %s", runtime.GOOS)
635+
}
636+
637+
t.Parallel()
638+
got := runTestProg(t, "testprogcgo", "Abort")
639+
t.Log(got)
640+
want := "SIGABRT"
641+
if !strings.Contains(got, want) {
642+
t.Errorf("did not see %q in output", want)
643+
}
644+
// No runtime errors like "runtime: unknown pc".
645+
nowant := "runtime: "
646+
if strings.Contains(got, nowant) {
647+
t.Errorf("did not see %q in output", want)
648+
}
649+
}
650+
606651
// TestEINTR tests that we handle EINTR correctly.
607652
// See issue #20400 and friends.
608653
func TestEINTR(t *testing.T) {

src/runtime/signal_unix.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,9 +688,11 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
688688
}
689689

690690
print("PC=", hex(c.sigpc()), " m=", _g_.m.id, " sigcode=", c.sigcode(), "\n")
691-
if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
691+
if _g_.m.incgo && gp == _g_.m.g0 && _g_.m.curg != nil {
692692
print("signal arrived during cgo execution\n")
693-
gp = _g_.m.lockedg.ptr()
693+
// Switch to curg so that we get a traceback of the Go code
694+
// leading up to the cgocall, which switched from curg to g0.
695+
gp = _g_.m.curg
694696
}
695697
if sig == _SIGILL || sig == _SIGFPE {
696698
// It would be nice to know how long the instruction is.

src/runtime/signal_windows.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,11 @@ func winthrow(info *exceptionrecord, r *context, gp *g) {
218218
print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n")
219219

220220
print("PC=", hex(r.ip()), "\n")
221-
if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
221+
if _g_.m.incgo && gp == _g_.m.g0 && _g_.m.curg != nil {
222222
if iscgo {
223223
print("signal arrived during external code execution\n")
224224
}
225-
gp = _g_.m.lockedg.ptr()
225+
gp = _g_.m.curg
226226
}
227227
print("\n")
228228

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import "C"
4+
5+
// This program will crash.
6+
// We want to test unwinding from a cgo callback.
7+
8+
/*
9+
void panic_callback();
10+
11+
static void call_callback(void) {
12+
panic_callback();
13+
}
14+
*/
15+
import "C"
16+
17+
func init() {
18+
register("PanicCallback", PanicCallback)
19+
}
20+
21+
//export panic_callback
22+
func panic_callback() {
23+
var i *int
24+
*i = 42
25+
}
26+
27+
func PanicCallback() {
28+
C.call_callback()
29+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
// This program will abort.
8+
9+
/*
10+
#include <stdlib.h>
11+
*/
12+
import "C"
13+
14+
func init() {
15+
register("Abort", Abort)
16+
}
17+
18+
func Abort() {
19+
C.abort()
20+
}

0 commit comments

Comments
 (0)