t.Logf("%s", out)
t.Errorf("got %v; expected SIGSEGV", ee)
}
+
+ // Test SIGPIPE forwarding
+ cmd = exec.Command(bin[0], append(bin[1:], "3")...)
+
+ out, err = cmd.CombinedOutput()
+
+ if err == nil {
+ t.Logf("%s", out)
+ t.Error("test program succeeded unexpectedly")
+ } else if ee, ok := err.(*exec.ExitError); !ok {
+ t.Logf("%s", out)
+ t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
+ } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
+ t.Logf("%s", out)
+ t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
+ } else if !ws.Signaled() || ws.Signal() != syscall.SIGPIPE {
+ t.Logf("%s", out)
+ t.Errorf("got %v; expected SIGPIPE", ee)
+ }
}
func TestSignalForwardingExternal(t *testing.T) {
#include <unistd.h>
#include <sched.h>
#include <time.h>
+#include <errno.h>
#include "libgo2.h"
}
static volatile sig_atomic_t sigioSeen;
+static volatile sig_atomic_t sigpipeSeen;
// Use up some stack space.
static void recur(int i, char *p) {
}
}
+static void pipeHandler(int signo, siginfo_t* info, void* ctxt) {
+ sigpipeSeen = 1;
+}
+
// Signal handler that uses up more stack space than a goroutine will have.
static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
char a[1024];
die("sigaction");
}
+ sa.sa_sigaction = pipeHandler;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+ die("sigaction");
+ }
}
int main(int argc, char** argv) {
nanosleep(&ts, NULL);
i++;
if (i > 5000) {
- fprintf(stderr, "looping too long waiting for signal\n");
+ fprintf(stderr, "looping too long waiting for SIGIO\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (verbose) {
+ printf("provoking SIGPIPE\n");
+ }
+
+ GoRaiseSIGPIPE();
+
+ if (verbose) {
+ printf("waiting for sigpipeSeen\n");
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (!sigpipeSeen) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for SIGPIPE\n");
exit(EXIT_FAILURE);
}
}
sigioSeen = 1;
}
+// Set up the SIGPIPE signal handler in a high priority constructor, so
+// that it is installed before the Go code starts.
+
+static void pipeHandler(int signo, siginfo_t* info, void* ctxt) {
+ const char *s = "unexpected SIGPIPE\n";
+ write(2, s, strlen(s));
+ exit(EXIT_FAILURE);
+}
+
+static void init(void) __attribute__ ((constructor (200)));
+
+static void init() {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_sigaction = pipeHandler;
+ if (sigemptyset(&sa.sa_mask) < 0) {
+ die("sigemptyset");
+ }
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+}
+
int main(int argc, char** argv) {
int verbose;
struct sigaction sa;
verbose = argc > 2;
setvbuf(stdout, NULL, _IONBF, 0);
+ if (verbose) {
+ printf("raising SIGPIPE\n");
+ }
+
+ // Test that the Go runtime handles SIGPIPE, even if we installed
+ // a non-default SIGPIPE handler before the runtime initializes.
+ ProvokeSIGPIPE();
+
if (verbose) {
printf("calling sigaction\n");
}
break;
}
+ case 3: {
+ if (verbose) {
+ printf("attempting SIGPIPE\n");
+ }
+
+ int fd[2];
+ if (pipe(fd) != 0) {
+ printf("pipe(2) failed\n");
+ return 0;
+ }
+ // Close the reading end.
+ close(fd[0]);
+ // Expect that write(2) fails (EPIPE)
+ if (write(fd[1], "some data", 9) != -1) {
+ printf("write(2) unexpectedly succeeded\n");
+ return 0;
+ }
+ }
default:
printf("Unknown test: %d\n", test);
return 0;
package main
+/*
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+// Raise SIGPIPE.
+static void CRaiseSIGPIPE() {
+ int fds[2];
+
+ if (pipe(fds) == -1) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ }
+ // Close the reader end
+ close(fds[0]);
+ // Write to the writer end to provoke a SIGPIPE
+ if (write(fds[1], "some data", 9) != -1) {
+ fprintf(stderr, "write to a closed pipe succeeded\n");
+ exit(EXIT_FAILURE);
+ }
+ close(fds[1]);
+}
+*/
import "C"
import (
func Noop() {
}
+// Raise SIGPIPE.
+//export GoRaiseSIGPIPE
+func GoRaiseSIGPIPE() {
+ C.CRaiseSIGPIPE()
+}
+
func main() {
}
}
}
+// ProvokeSIGPIPE provokes a kernel-initiated SIGPIPE.
+//export ProvokeSIGPIPE
+func ProvokeSIGPIPE() {
+ r, w, err := os.Pipe()
+ if err != nil {
+ panic(err)
+ }
+ r.Close()
+ defer w.Close()
+ w.Write([]byte("some data"))
+}
+
func main() {
}
SIGSETXID signals (which are used only on GNU/Linux), it will turn on
the SA_ONSTACK flag and otherwise keep the signal handler.
-For the synchronous signals, the Go runtime will install a signal
-handler. It will save any existing signal handler. If a synchronous
-signal arrives while executing non-Go code, the Go runtime will invoke
-the existing signal handler instead of the Go signal handler.
+For the synchronous signals and SIGPIPE, the Go runtime will install a
+signal handler. It will save any existing signal handler. If a
+synchronous signal arrives while executing non-Go code, the Go runtime
+will invoke the existing signal handler instead of the Go signal
+handler.
Go code built with -buildmode=c-archive or -buildmode=c-shared will
not install any other signal handlers by default. If there is an
mp := getg().m
mp.ncgocall++
mp.ncgo++
+ mp.incgo = true
// Reset traceback.
mp.cgoCallers[0] = 0
//go:nosplit
func endcgo(mp *m) {
+ mp.incgo = false
mp.ncgo--
if raceenabled {
savedsp := unsafe.Pointer(gp.syscallsp)
savedpc := gp.syscallpc
exitsyscall(0) // coming out of cgo call
+ gp.m.incgo = false
cgocallbackg1(ctxt)
+ gp.m.incgo = true
// going back to cgo call
reentersyscall(savedpc, uintptr(savedsp))
inwb bool // m is executing a write barrier
newSigstack bool // minit on C thread called sigaltstack
printlock int8
+ incgo bool // m is executing a cgo call
fastrand uint32
ncgocall uint64 // number of cgo calls in total
ncgo int32 // number of cgo calls currently in progress
}
// 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
}
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.