Skip to content

Commit e5bd6e1

Browse files
runtime: crash on SI_USER SigPanic signal
Clean up the code a little bit to make it clearer: Don't check throwsplit for a SI_USER signal. If throwsplit is set for a SigPanic signal, always throw; discard any other flags. Fixes #36420 Change-Id: Ic9dcd1108603d241f71c040504dfdc6e528f9767 Reviewed-on: https://go-review.googlesource.com/c/go/+/228900 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent 5a75f7c commit e5bd6e1

File tree

4 files changed

+96
-3
lines changed

4 files changed

+96
-3
lines changed

Diff for: doc/go1.15.html

+11
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,17 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
198198
<code>uint</code>, <code>uint8</code>, <code>uint16</code>, <code>uint32</code>, <code>uint64</code>, <code>uintptr</code>,
199199
then the value will be printed, instead of just its address.
200200
</p>
201+
202+
<p><!-- CL -->
203+
On a Unix system, if the <code>kill</code> command
204+
or <code>kill</code> system call is used to send
205+
a <code>SIGSEGV</code>, <code>SIGBUS</code>,
206+
or <code>SIGFPE</code> signal to a Go program, and if the signal
207+
is not being handled via
208+
<a href="/pkg/os/signal/#Notify"><code>os/signal.Notify</code></a>,
209+
the Go program will now reliably crash with a stack trace.
210+
In earlier releases the behavior was unpredictable.
211+
</p>
201212
</dd>
202213
</dl>
203214

Diff for: src/runtime/crash_cgo_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,21 @@ func findTrace(text, top string) []string {
555555
}
556556
return nil
557557
}
558+
559+
func TestSegv(t *testing.T) {
560+
switch runtime.GOOS {
561+
case "plan9", "windows":
562+
t.Skipf("no signals on %s", runtime.GOOS)
563+
}
564+
565+
for _, test := range []string{"Segv", "SegvInCgo"} {
566+
t.Run(test, func(t *testing.T) {
567+
t.Parallel()
568+
got := runTestProg(t, "testprogcgo", test)
569+
t.Log(got)
570+
if !strings.Contains(got, "SIGSEGV") {
571+
t.Errorf("expected crash from signal")
572+
}
573+
})
574+
}
575+
}

Diff for: src/runtime/signal_unix.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -546,10 +546,10 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
546546
if sig < uint32(len(sigtable)) {
547547
flags = sigtable[sig].flags
548548
}
549-
if flags&_SigPanic != 0 && gp.throwsplit {
549+
if c.sigcode() != _SI_USER && flags&_SigPanic != 0 && gp.throwsplit {
550550
// We can't safely sigpanic because it may grow the
551551
// stack. Abort in the signal handler instead.
552-
flags = (flags &^ _SigPanic) | _SigThrow
552+
flags = _SigThrow
553553
}
554554
if isAbortPC(c.sigpc()) {
555555
// On many architectures, the abort function just
@@ -588,7 +588,11 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
588588
dieFromSignal(sig)
589589
}
590590

591-
if flags&_SigThrow == 0 {
591+
// _SigThrow means that we should exit now.
592+
// If we get here with _SigPanic, it means that the signal
593+
// was sent to us by a program (c.sigcode() == _SI_USER);
594+
// in that case, if we didn't handle it in sigsend, we exit now.
595+
if flags&(_SigThrow|_SigPanic) == 0 {
592596
return
593597
}
594598

Diff for: src/runtime/testdata/testprogcgo/segv.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2020 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+
// +build !plan9,!windows
6+
7+
package main
8+
9+
// static void nop() {}
10+
import "C"
11+
12+
import (
13+
"sync"
14+
"syscall"
15+
)
16+
17+
func init() {
18+
register("Segv", Segv)
19+
register("SegvInCgo", SegvInCgo)
20+
}
21+
22+
var Sum int
23+
24+
func Segv() {
25+
c := make(chan bool)
26+
var wg sync.WaitGroup
27+
wg.Add(1)
28+
go func() {
29+
defer wg.Done()
30+
close(c)
31+
for i := 0; i < 10000; i++ {
32+
Sum += i
33+
}
34+
}()
35+
36+
<-c
37+
38+
syscall.Kill(syscall.Getpid(), syscall.SIGSEGV)
39+
40+
wg.Wait()
41+
}
42+
43+
func SegvInCgo() {
44+
c := make(chan bool)
45+
var wg sync.WaitGroup
46+
wg.Add(1)
47+
go func() {
48+
defer wg.Done()
49+
close(c)
50+
for i := 0; i < 10000; i++ {
51+
C.nop()
52+
}
53+
}()
54+
55+
<-c
56+
57+
syscall.Kill(syscall.Getpid(), syscall.SIGSEGV)
58+
59+
wg.Wait()
60+
}

0 commit comments

Comments
 (0)