runtime: handle SIGPIPE in c-archive and c-shared programs

Before this CL, Go programs in c-archive or c-shared buildmodes
would not handle SIGPIPE. That leads to surprising behaviour where
writes on a closed pipe or socket would raise SIGPIPE and terminate
the program. This CL changes the Go runtime to handle
SIGPIPE regardless of buildmode. In addition, SIGPIPE from non-Go
code is forwarded.

This is a refinement of CL 32796 that fixes the case where a non-default
handler for SIGPIPE is installed by the host C program.

Fixes #17393

Change-Id: Ia41186e52c1ac209d0a594bae9904166ae7df7de
Reviewed-on: https://go-review.googlesource.com/35960
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Elias Naur 2017-01-29 15:34:50 +01:00
parent b612ab3acb
commit 78074f6850
10 changed files with 165 additions and 11 deletions

View file

@ -111,8 +111,8 @@ func sigInstallGoHandler(sig uint32) bool {
}
// When built using c-archive or c-shared, only install signal
// handlers for synchronous signals.
if (isarchive || islibrary) && t.flags&_SigPanic == 0 {
// handlers for synchronous signals and SIGPIPE.
if (isarchive || islibrary) && t.flags&_SigPanic == 0 && sig != _SIGPIPE {
return false
}
@ -518,16 +518,19 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
return true
}
// Only forward synchronous signals.
c := &sigctxt{info, ctx}
if c.sigcode() == _SI_USER || flags&_SigPanic == 0 {
// Only forward synchronous signals and SIGPIPE.
// 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 {
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).
// (2) we weren't in CGO.
g := getg()
if g != nil && g.m != nil && g.m.curg != nil && g.m.curg.syscallsp == 0 {
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
return false
}
// Signal not handled by Go, forward it.