Skip to content

Commit

Permalink
runtime: crash on SI_USER SigPanic signal
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
ianlancetaylor committed Apr 22, 2020
1 parent 5a75f7c commit e5bd6e1
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 3 deletions.
11 changes: 11 additions & 0 deletions doc/go1.15.html
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
<code>uint</code>, <code>uint8</code>, <code>uint16</code>, <code>uint32</code>, <code>uint64</code>, <code>uintptr</code>,
then the value will be printed, instead of just its address.
</p>

<p><!-- CL -->
On a Unix system, if the <code>kill</code> command
or <code>kill</code> system call is used to send
a <code>SIGSEGV</code>, <code>SIGBUS</code>,
or <code>SIGFPE</code> signal to a Go program, and if the signal
is not being handled via
<a href="/pkg/os/signal/#Notify"><code>os/signal.Notify</code></a>,
the Go program will now reliably crash with a stack trace.
In earlier releases the behavior was unpredictable.
</p>
</dd>
</dl>

Expand Down
18 changes: 18 additions & 0 deletions src/runtime/crash_cgo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,3 +555,21 @@ func findTrace(text, top string) []string {
}
return nil
}

func TestSegv(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("no signals on %s", runtime.GOOS)
}

for _, test := range []string{"Segv", "SegvInCgo"} {
t.Run(test, func(t *testing.T) {
t.Parallel()
got := runTestProg(t, "testprogcgo", test)
t.Log(got)
if !strings.Contains(got, "SIGSEGV") {
t.Errorf("expected crash from signal")
}
})
}
}
10 changes: 7 additions & 3 deletions src/runtime/signal_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,10 +546,10 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
if sig < uint32(len(sigtable)) {
flags = sigtable[sig].flags
}
if flags&_SigPanic != 0 && gp.throwsplit {
if c.sigcode() != _SI_USER && flags&_SigPanic != 0 && gp.throwsplit {
// We can't safely sigpanic because it may grow the
// stack. Abort in the signal handler instead.
flags = (flags &^ _SigPanic) | _SigThrow
flags = _SigThrow
}
if isAbortPC(c.sigpc()) {
// On many architectures, the abort function just
Expand Down Expand Up @@ -588,7 +588,11 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
dieFromSignal(sig)
}

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

Expand Down
60 changes: 60 additions & 0 deletions src/runtime/testdata/testprogcgo/segv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !plan9,!windows

package main

// static void nop() {}
import "C"

import (
"sync"
"syscall"
)

func init() {
register("Segv", Segv)
register("SegvInCgo", SegvInCgo)
}

var Sum int

func Segv() {
c := make(chan bool)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
close(c)
for i := 0; i < 10000; i++ {
Sum += i
}
}()

<-c

syscall.Kill(syscall.Getpid(), syscall.SIGSEGV)

wg.Wait()
}

func SegvInCgo() {
c := make(chan bool)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
close(c)
for i := 0; i < 10000; i++ {
C.nop()
}
}()

<-c

syscall.Kill(syscall.Getpid(), syscall.SIGSEGV)

wg.Wait()
}

0 comments on commit e5bd6e1

Please sign in to comment.