1
0
mirror of https://github.com/golang/go synced 2024-11-26 11:28:21 -07:00

runtime: treat SI_TKILL like SI_USER on Linux

On Linux a signal sent using tgkill will have si_code == SI_TKILL,
not SI_USER. Treat the two cases the same. Add a Linux-specific test.

Change the test to use the C pause function rather than sleeping
for a second, as that achieves the same effect.

Change-Id: I2a36646aecabcab9ec42ed9a048b07c2ff0a3987
Reviewed-on: https://go-review.googlesource.com/c/go/+/431255
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Ian Lance Taylor 2022-09-15 11:33:58 -07:00 committed by Gopher Robot
parent a9ca741d31
commit 7c87012f04
10 changed files with 107 additions and 22 deletions

View File

@ -603,8 +603,14 @@ func TestSegv(t *testing.T) {
t.Skipf("no signals on %s", runtime.GOOS)
}
for _, test := range []string{"Segv", "SegvInCgo"} {
for _, test := range []string{"Segv", "SegvInCgo", "TgkillSegv", "TgkillSegvInCgo"} {
test := test
// The tgkill variants only run on Linux.
if runtime.GOOS != "linux" && strings.HasPrefix(test, "Tgkill") {
continue
}
t.Run(test, func(t *testing.T) {
t.Parallel()
got := runTestProg(t, "testprogcgo", test)
@ -617,6 +623,15 @@ func TestSegv(t *testing.T) {
t.Errorf("did not see %q in output", want)
}
doNotWant := "fatal error:"
if strings.Contains(got, doNotWant) {
if runtime.GOOS == "darwin" && strings.Contains(got, "0xb01dfacedebac1e") {
// See the comment in signal_darwin_amd64.go.
t.Skip("skipping due to Darwin handling of malformed addresses")
}
t.Errorf("saw %q in output", doNotWant)
}
// No runtime errors like "runtime: unknown pc".
switch runtime.GOOS {
case "darwin", "illumos", "solaris":

View File

@ -886,3 +886,15 @@ func runPerThreadSyscall() {
gp.m.needPerThreadSyscall.Store(0)
}
const (
_SI_USER = 0
_SI_TKILL = -6
)
// sigFromUser reports whether the signal was sent because of a call
// to kill or tgkill.
func (c *sigctxt) sigFromUser() bool {
code := int32(c.sigcode())
return code == _SI_USER || code == _SI_TKILL
}

View File

@ -11,7 +11,6 @@ package runtime
const (
_SS_DISABLE = 2
_NSIG = 65
_SI_USER = 0
_SIG_BLOCK = 0
_SIG_UNBLOCK = 1
_SIG_SETMASK = 2

View File

@ -9,7 +9,6 @@ package runtime
const (
_SS_DISABLE = 2
_NSIG = 65
_SI_USER = 0
_SIG_BLOCK = 0
_SIG_UNBLOCK = 1
_SIG_SETMASK = 2

View File

@ -27,7 +27,6 @@ func cputicks() int64 {
const (
_SS_DISABLE = 2
_NSIG = 129
_SI_USER = 0
_SIG_BLOCK = 1
_SIG_UNBLOCK = 2
_SIG_SETMASK = 3

View File

@ -21,7 +21,6 @@ func cputicks() int64 {
const (
_SS_DISABLE = 2
_NSIG = 128 + 1
_SI_USER = 0
_SIG_BLOCK = 1
_SIG_UNBLOCK = 2
_SIG_SETMASK = 3

View File

@ -0,0 +1,13 @@
// Copyright 2022 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.
//go:build unix && !linux
package runtime
// sigFromUser reports whether the signal was sent because of a call
// to kill.
func (c *sigctxt) sigFromUser() bool {
return c.sigcode() == _SI_USER
}

View File

@ -662,7 +662,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
if sig < uint32(len(sigtable)) {
flags = sigtable[sig].flags
}
if c.sigcode() != _SI_USER && flags&_SigPanic != 0 && gp.throwsplit {
if !c.sigFromUser() && flags&_SigPanic != 0 && gp.throwsplit {
// We can't safely sigpanic because it may grow the
// stack. Abort in the signal handler instead.
flags = _SigThrow
@ -672,7 +672,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
// causes a memory fault. Don't turn that into a panic.
flags = _SigThrow
}
if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
if !c.sigFromUser() && flags&_SigPanic != 0 {
// The signal is going to cause a panic.
// Arrange the stack so that it looks like the point
// where the signal occurred made a call to the
@ -690,13 +690,13 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
return
}
if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
if c.sigFromUser() || flags&_SigNotify != 0 {
if sigsend(sig) {
return
}
}
if c.sigcode() == _SI_USER && signal_ignored(sig) {
if c.sigFromUser() && signal_ignored(sig) {
return
}
@ -706,7 +706,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
// _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);
// was sent to us by a program (c.sigFromUser() is true);
// in that case, if we didn't handle it in sigsend, we exit now.
if flags&(_SigThrow|_SigPanic) == 0 {
return
@ -929,7 +929,7 @@ func raisebadsignal(sig uint32, c *sigctxt) {
//
// On FreeBSD, the libthr sigaction code prevents
// this from working so we fall through to raise.
if GOOS != "freebsd" && (isarchive || islibrary) && handler == _SIG_DFL && c.sigcode() != _SI_USER {
if GOOS != "freebsd" && (isarchive || islibrary) && handler == _SIG_DFL && !c.sigFromUser() {
return
}
@ -1110,7 +1110,7 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
// Unfortunately, user generated SIGPIPEs will also be forwarded, because si_code
// is set to _SI_USER even for a SIGPIPE raised from a write to a closed socket
// or pipe.
if (c.sigcode() == _SI_USER || flags&_SigPanic == 0) && sig != _SIGPIPE {
if (c.sigFromUser() || flags&_SigPanic == 0) && sig != _SIGPIPE {
return false
}
// Determine if the signal occurred inside Go code. We test that:

View File

@ -2,18 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !plan9 && !windows
// +build !plan9,!windows
//go:build unix
// +build unix
package main
// #include <unistd.h>
// static void nop() {}
import "C"
import (
"syscall"
"time"
)
import "syscall"
func init() {
register("Segv", Segv)
@ -35,8 +33,8 @@ func Segv() {
syscall.Kill(syscall.Getpid(), syscall.SIGSEGV)
// Give the OS time to deliver the signal.
time.Sleep(time.Second)
// Wait for the OS to deliver the signal.
C.pause()
}
func SegvInCgo() {
@ -52,6 +50,6 @@ func SegvInCgo() {
syscall.Kill(syscall.Getpid(), syscall.SIGSEGV)
// Give the OS time to deliver the signal.
time.Sleep(time.Second)
// Wait for the OS to deliver the signal.
C.pause()
}

View File

@ -0,0 +1,51 @@
// Copyright 2022 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.
package main
// #include <unistd.h>
// static void nop() {}
import "C"
import "syscall"
func init() {
register("TgkillSegv", TgkillSegv)
register("TgkillSegvInCgo", TgkillSegvInCgo)
}
func TgkillSegv() {
c := make(chan bool)
go func() {
close(c)
for i := 0; ; i++ {
// Sum defined in segv.go.
Sum += i
}
}()
<-c
syscall.Tgkill(syscall.Getpid(), syscall.Gettid(), syscall.SIGSEGV)
// Wait for the OS to deliver the signal.
C.pause()
}
func TgkillSegvInCgo() {
c := make(chan bool)
go func() {
close(c)
for {
C.nop()
}
}()
<-c
syscall.Tgkill(syscall.Getpid(), syscall.Gettid(), syscall.SIGSEGV)
// Wait for the OS to deliver the signal.
C.pause()
}