From: Srdjan Petrovic Date: Thu, 9 Apr 2015 18:12:12 +0000 (-0700) Subject: runtime: signal forwarding X-Git-Tag: go1.5beta1~936 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=5c8fbc6f1e4ba78133c53ce73f82ad10e81b42f8;p=gostls13.git runtime: signal forwarding Forward signals to signal handlers installed before Go installs its own, under certain circumstances. In particular, as iant@ suggests, signals are forwarded iff: (1) a non-SIG_DFL signal handler existed before Go, and (2) signal is synchronous (i.e., one of SIGSEGV, SIGBUS, SIGFPE), and (3a) signal occured on a non-Go thread, or (3b) signal occurred on a Go thread but in CGo code. Supported only on Linux, for now. Change-Id: I403219ee47b26cf65da819fb86cf1ec04d3e25f5 Reviewed-on: https://go-review.googlesource.com/8712 Reviewed-by: Ian Lance Taylor --- diff --git a/misc/cgo/testsigfwd/main.go b/misc/cgo/testsigfwd/main.go new file mode 100644 index 0000000000..6641c9d4ee --- /dev/null +++ b/misc/cgo/testsigfwd/main.go @@ -0,0 +1,58 @@ +// Copyright 2015 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 + +import "fmt" + +/* +#include +#include +#include + +int *p; +static void sigsegv() { + *p = 1; + fprintf(stderr, "ERROR: C SIGSEGV not thrown on caught?.\n"); + exit(2); +} + +static void sighandler(int signum) { + if (signum == SIGSEGV) { + exit(0); // success + } +} + +static void __attribute__ ((constructor)) sigsetup(void) { + struct sigaction act; + act.sa_handler = &sighandler; + sigaction(SIGSEGV, &act, 0); +} +*/ +import "C" + +var p *byte + +func f() (ret bool) { + defer func() { + if recover() == nil { + fmt.Errorf("ERROR: couldn't raise SIGSEGV in Go.") + C.exit(2) + } + ret = true + }() + *p = 1 + return false +} + +func main() { + // Test that the signal originating in Go is handled (and recovered) by Go. + if !f() { + fmt.Errorf("couldn't recover from SIGSEGV in Go.") + C.exit(2) + } + + // Test that the signal originating in C is handled by C. + C.sigsegv() +} diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index f4fef5655d..559e5aaf3a 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -284,6 +284,9 @@ func (t *tester) registerTests() { if t.hasBash() && t.goos != "android" && !iOS && t.gohostos != "windows" { t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash") } + if t.gohostos == "linux" && t.extLink() { + t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go") + } } if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !iOS { t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go") diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index abea5d61aa..523d28b210 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -18,6 +18,9 @@ func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 //go:noescape func sigaltstack(new, old *sigaltstackt) +//go:noescape +func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer) + //go:noescape func setitimer(mode int32, new, old *itimerval) diff --git a/src/runtime/signal1_unix.go b/src/runtime/signal1_unix.go index 184fd125fb..7577d43a64 100644 --- a/src/runtime/signal1_unix.go +++ b/src/runtime/signal1_unix.go @@ -11,6 +11,14 @@ const ( _SIG_IGN uintptr = 1 ) +// Stores the signal handlers registered before Go installed its own. +// These signal handlers will be invoked in cases where Go doesn't want to +// handle a particular signal (e.g., signal occurred on a non-Go thread). +// See sigfwdgo() for more information on when the signals are forwarded. +// +// Signal forwarding is currently available only on Linux. +var fwdSig [_NSIG]uintptr + func initsig() { // _NSIG is the number of signals on this operating system. // sigtable should describe what to do for all the possible signals. @@ -25,7 +33,7 @@ func initsig() { if t.flags == 0 || t.flags&_SigDefault != 0 { continue } - + fwdSig[i] = getsig(i) // For some signals, we respect an inherited SIG_IGN handler // rather than insist on installing our own default handler. // Even these signals can be fetched using the os/signal package. diff --git a/src/runtime/signal_linux.go b/src/runtime/signal_linux.go index c71e619b1e..1ab4e9ec71 100644 --- a/src/runtime/signal_linux.go +++ b/src/runtime/signal_linux.go @@ -4,6 +4,8 @@ package runtime +import "unsafe" + type sigTabT struct { flags int32 name string @@ -76,3 +78,53 @@ var sigtable = [...]sigTabT{ /* 63 */ {_SigNotify, "signal 63"}, /* 64 */ {_SigNotify, "signal 64"}, } + +// Determines if the signal should be handled by Go and if not, forwards the +// signal to the handler that was installed before Go's. Returns whether the +// signal was forwarded. +//go:nosplit +func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool { + g := getg() + c := &sigctxt{info, ctx} + if sig >= uint32(len(sigtable)) { + return false + } + fwdFn := fwdSig[sig] + flags := sigtable[sig].flags + + // If there is no handler to forward to, no need to forward. + if fwdFn == _SIG_DFL { + return false + } + // Only forward synchronous signals. + if c.sigcode() == _SI_USER || flags&_SigPanic == 0 { + return false + } + // Determine if the signal occurred inside Go code. We test that: + // (1) we were in a goroutine (i.e., m.curg != nil), and + // (2) we weren't in CGO (i.e., m.curg.syscallsp == 0). + if g != nil && g.m != nil && g.m.curg != nil && g.m.curg.syscallsp == 0 { + return false + } + // Signal not handled by Go, forward it. + if fwdFn != _SIG_IGN { + sigfwd(fwdFn, sig, info, ctx) + } + return true +} + +// Continuation of the (assembly) sigtramp() logic. +//go:nosplit +func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { + if sigfwdgo(sig, info, ctx) { + return + } + g := getg() + if g == nil { + badsignal(uintptr(sig)) + return + } + setg(g.m.gsignal) + sighandler(sig, info, ctx, g) + setg(g) +} diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s index 679a81d66d..f5cfb644c9 100644 --- a/src/runtime/sys_linux_386.s +++ b/src/runtime/sys_linux_386.s @@ -191,43 +191,25 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0 MOVL AX, ret+16(FP) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$44 - get_tls(CX) - - // check that g exists - MOVL g(CX), DI - CMPL DI, $0 - JNE 6(PC) - MOVL sig+0(FP), BX - MOVL BX, 0(SP) - MOVL $runtime·badsignal(SB), AX +TEXT runtime·sigfwd(SB),NOSPLIT,$12-16 + MOVL sig+4(FP), AX + MOVL AX, 0(SP) + MOVL info+8(FP), AX + MOVL AX, 4(SP) + MOVL ctx+12(FP), AX + MOVL AX, 8(SP) + MOVL fn+0(FP), AX CALL AX RET - // save g - MOVL DI, 20(SP) - - // g = m->gsignal - MOVL g_m(DI), BX - MOVL m_gsignal(BX), BX - MOVL BX, g(CX) - - // copy arguments for call to sighandler +TEXT runtime·sigtramp(SB),NOSPLIT,$12 MOVL sig+0(FP), BX MOVL BX, 0(SP) MOVL info+4(FP), BX MOVL BX, 4(SP) MOVL context+8(FP), BX MOVL BX, 8(SP) - MOVL DI, 12(SP) - - CALL runtime·sighandler(SB) - - // restore g - get_tls(CX) - MOVL 20(SP), BX - MOVL BX, g(CX) - + CALL runtime·sigtrampgo(SB) RET TEXT runtime·sigreturn(SB),NOSPLIT,$0 diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index fa7fa164b5..f36ac8493b 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -212,37 +212,20 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36 MOVL AX, ret+32(FP) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$64 - get_tls(BX) - - // check that g exists - MOVQ g(BX), R10 - CMPQ R10, $0 - JNE 5(PC) - MOVQ DI, 0(SP) - MOVQ $runtime·badsignal(SB), AX +TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 + MOVQ sig+8(FP), DI + MOVQ info+16(FP), SI + MOVQ ctx+24(FP), DX + MOVQ fn+0(FP), AX CALL AX RET - // save g - MOVQ R10, 40(SP) - - // g = m->gsignal - MOVQ g_m(R10), AX - MOVQ m_gsignal(AX), AX - MOVQ AX, g(BX) - - MOVQ DI, 0(SP) - MOVQ SI, 8(SP) - MOVQ DX, 16(SP) - MOVQ R10, 24(SP) - - CALL runtime·sighandler(SB) - - // restore g - get_tls(BX) - MOVQ 40(SP), R10 - MOVQ R10, g(BX) +TEXT runtime·sigtramp(SB),NOSPLIT,$24 + MOVQ DI, 0(SP) // signum + MOVQ SI, 8(SP) // info + MOVQ DX, 16(SP) // ctx + MOVQ $runtime·sigtrampgo(SB), AX + CALL AX RET TEXT runtime·sigreturn(SB),NOSPLIT,$0 diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s index d0c6d22f31..3936cd93a8 100644 --- a/src/runtime/sys_linux_arm.s +++ b/src/runtime/sys_linux_arm.s @@ -327,7 +327,15 @@ TEXT runtime·sigaltstack(SB),NOSPLIT,$0 MOVW.HI R8, (R8) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$24 +TEXT runtime·sigfwd(SB),NOSPLIT,$0-16 + MOVW sig+4(FP), R0 + MOVW info+8(FP), R1 + MOVW ctx+12(FP), R2 + MOVW fn+0(FP), R11 + BL (R11) + RET + +TEXT runtime·sigtramp(SB),NOSPLIT,$12 // this might be called in external code context, // where g is not set. // first save R0, because runtime·load_g will clobber it @@ -336,32 +344,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$24 CMP $0, R0 BL.NE runtime·load_g(SB) - CMP $0, g - BNE 4(PC) - // signal number is already prepared in 4(R13) - MOVW $runtime·badsignal(SB), R11 - BL (R11) - RET - - // save g - MOVW g, R3 - MOVW g, 20(R13) - - // g = m->gsignal - MOVW g_m(g), R8 - MOVW m_gsignal(R8), g - - // copy arguments for call to sighandler - // R0 is already saved above MOVW R1, 8(R13) MOVW R2, 12(R13) - MOVW R3, 16(R13) - - BL runtime·sighandler(SB) - - // restore g - MOVW 20(R13), g - + MOVW $runtime·sigtrampgo(SB), R11 + BL (R11) RET TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0 diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s index 389fea0dde..0aca3a2010 100644 --- a/src/runtime/sys_linux_arm64.s +++ b/src/runtime/sys_linux_arm64.s @@ -215,7 +215,15 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36 MOVW R0, ret+32(FP) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$64 +TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 + MOVW sig+8(FP), R0 + MOVD info+16(FP), R1 + MOVD ctx+24(FP), R2 + MOVD fn+0(FP), R11 + BL (R11) + RET + +TEXT runtime·sigtramp(SB),NOSPLIT,$24 // this might be called in external code context, // where g is not set. // first save R0, because runtime·load_g will clobber it @@ -225,31 +233,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64 BEQ 2(PC) BL runtime·load_g(SB) - // check that g exists - CMP g, ZR - BNE ok - MOVD $runtime·badsignal(SB), R0 - BL (R0) - RET - -ok: - // save g - MOVD g, 40(RSP) - MOVD g, R6 - - // g = m->gsignal - MOVD g_m(g), R7 - MOVD m_gsignal(R7), g - - // R0 is already saved above MOVD R1, 16(RSP) MOVD R2, 24(RSP) - MOVD R6, 32(RSP) - - BL runtime·sighandler(SB) - - // restore g - MOVD 40(RSP), g + MOVD $runtime·sigtrampgo(SB), R0 + BL (R0) RET TEXT runtime·mmap(SB),NOSPLIT,$-8 diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s index 44a22c9c77..232f299ac2 100644 --- a/src/runtime/sys_linux_ppc64x.s +++ b/src/runtime/sys_linux_ppc64x.s @@ -196,6 +196,15 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36 MOVW R3, ret+32(FP) RETURN +TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 + MOVW sig+8(FP), R3 + MOVD info+16(FP), R4 + MOVD ctx+24(FP), R5 + MOVD fn+0(FP), R31 + MOVD R31, CTR + BL (CTR) + RETURN + #ifdef GOARCH_ppc64le // ppc64le doesn't need function descriptors TEXT runtime·sigtramp(SB),NOSPLIT,$64 @@ -217,33 +226,12 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64 BEQ 2(PC) BL runtime·load_g(SB) - // check that g exists - CMP g, $0 - BNE 6(PC) - MOVD R3, 8(R1) - MOVD $runtime·badsignal(SB), R31 - MOVD R31, CTR - BL (CTR) - RETURN - - // save g - MOVD g, 40(R1) - MOVD g, R6 - - // g = m->gsignal - MOVD g_m(g), R7 - MOVD m_gsignal(R7), g - MOVW R3, 8(R1) MOVD R4, 16(R1) MOVD R5, 24(R1) - MOVD R6, 32(R1) - - BL runtime·sighandler(SB) - - // restore g - MOVD 40(R1), g - + MOVD $runtime·sigtrampgo(SB), R31 + MOVD R31, CTR + BL (CTR) RETURN TEXT runtime·mmap(SB),NOSPLIT,$-8