]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: don't forward SIGPIPE on macOS
authorElias Naur <mail@eliasnaur.com>
Wed, 31 Jul 2019 12:33:57 +0000 (14:33 +0200)
committerElias Naur <mail@eliasnaur.com>
Sat, 31 Aug 2019 06:19:40 +0000 (06:19 +0000)
macOS and iOS deliver SIGPIPE signals to the main thread and not
the thread that raised it by writing to a closed socket or pipe.

SIGPIPE signals can be suppressed for sockets with the SO_NOSIGPIPE
option, but there is no similar option for pipes. We have no other
choice but to never forward SIGPIPE on macOS.

This is a fixup of reverted CL 188297.

Fixes #33384

Change-Id: I09b258b078857ad3b22025bc2902d1b12d2afd92
Reviewed-on: https://go-review.googlesource.com/c/go/+/191785
Run-TryBot: Elias Naur <mail@eliasnaur.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
misc/cgo/testcarchive/carchive_test.go
misc/cgo/testcarchive/testdata/main2.c
misc/cgo/testcarchive/testdata/main3.c
src/runtime/signal_unix.go

index 381239ab7980172dcc6838f70847024f716ca54a..739bfe42bf56fa873c711b97cd9f1ce421dde3bd 100644 (file)
@@ -282,7 +282,13 @@ func TestEarlySignalHandler(t *testing.T) {
                t.Fatal(err)
        }
 
-       if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
+       darwin := "0"
+       if runtime.GOOS == "darwin" {
+               darwin = "1"
+       }
+       cmd = exec.Command(bin[0], append(bin[1:], darwin)...)
+
+       if out, err := cmd.CombinedOutput(); err != nil {
                t.Logf("%s", out)
                t.Fatal(err)
        }
@@ -320,12 +326,15 @@ func TestSignalForwarding(t *testing.T) {
        t.Logf("%s", out)
        expectSignal(t, err, syscall.SIGSEGV)
 
-       // Test SIGPIPE forwarding
-       cmd = exec.Command(bin[0], append(bin[1:], "3")...)
+       // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
+       if runtime.GOOS != "darwin" {
+               // Test SIGPIPE forwarding
+               cmd = exec.Command(bin[0], append(bin[1:], "3")...)
 
-       out, err = cmd.CombinedOutput()
-       t.Logf("%s", out)
-       expectSignal(t, err, syscall.SIGPIPE)
+               out, err = cmd.CombinedOutput()
+               t.Logf("%s", out)
+               expectSignal(t, err, syscall.SIGPIPE)
+       }
 }
 
 func TestSignalForwardingExternal(t *testing.T) {
@@ -744,11 +753,20 @@ func TestCompileWithoutShared(t *testing.T) {
        }
        defer os.Remove(exe)
 
-       binArgs := append(cmdToRun(exe), "3")
+       binArgs := append(cmdToRun(exe), "1")
        t.Log(binArgs)
        out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
        t.Logf("%s", out)
-       expectSignal(t, err, syscall.SIGPIPE)
+       expectSignal(t, err, syscall.SIGSEGV)
+
+       // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
+       if runtime.GOOS != "darwin" {
+               binArgs := append(cmdToRun(exe), "3")
+               t.Log(binArgs)
+               out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
+               t.Logf("%s", out)
+               expectSignal(t, err, syscall.SIGPIPE)
+       }
 }
 
 // Test that installing a second time recreates the header files.
index 769cd497e6c1d2e1a8431db41675f38feaf2e8ff..da35673421b39a1b774f625fe654b949dd8d55df 100644 (file)
@@ -123,8 +123,12 @@ int main(int argc, char** argv) {
        sigset_t mask;
        int i;
        struct timespec ts;
+       int darwin;
+
+       darwin = atoi(argv[1]);
+
+       verbose = argc > 2;
 
-       verbose = argc > 1;
        setvbuf(stdout, NULL, _IONBF, 0);
 
        // Call setsid so that we can use kill(0, SIGIO) below.
@@ -186,22 +190,25 @@ int main(int argc, char** argv) {
                printf("provoking SIGPIPE\n");
        }
 
-       GoRaiseSIGPIPE();
+       // SIGPIPE is never forwarded on Darwin, see golang.org/issue/33384.
+       if (!darwin) {
+               GoRaiseSIGPIPE();
 
-       if (verbose) {
-               printf("waiting for sigpipeSeen\n");
-       }
+               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);
+               // 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);
+                       }
                }
        }
 
index 60a16cf5fc464ffb13aaf3076694be4a83d7f826..4d11d9ce4c33267cf23f7e63294c1d0bacfc5128 100644 (file)
@@ -12,6 +12,7 @@
 #include <time.h>
 #include <sched.h>
 #include <unistd.h>
+#include <pthread.h>
 
 #include "libgo3.h"
 
@@ -51,11 +52,18 @@ static void init() {
        }
 }
 
+static void *provokeSIGPIPE(void *arg) {
+       ProvokeSIGPIPE();
+       return NULL;
+}
+
 int main(int argc, char** argv) {
        int verbose;
        struct sigaction sa;
        int i;
        struct timespec ts;
+       int res;
+       pthread_t tid;
 
        verbose = argc > 2;
        setvbuf(stdout, NULL, _IONBF, 0);
@@ -68,6 +76,19 @@ int main(int argc, char** argv) {
        // a non-default SIGPIPE handler before the runtime initializes.
        ProvokeSIGPIPE();
 
+       // Test that SIGPIPE on a non-main thread is also handled by Go.
+       res = pthread_create(&tid, NULL, provokeSIGPIPE, NULL);
+       if (res != 0) {
+               fprintf(stderr, "pthread_create: %s\n", strerror(res));
+               exit(EXIT_FAILURE);
+       }
+
+       res = pthread_join(tid, NULL);
+       if (res != 0) {
+               fprintf(stderr, "pthread_join: %s\n", strerror(res));
+               exit(EXIT_FAILURE);
+       }
+
        if (verbose) {
                printf("calling sigaction\n");
        }
index ad51dc1800fef0746f705994a64dd4082ad61d9b..436c18c1261f5443f829b22a17bbb83cce9386c5 100644 (file)
@@ -636,6 +636,13 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
                return true
        }
 
+       // This function and its caller sigtrampgo assumes SIGPIPE is delivered on the
+       // originating thread. This property does not hold on macOS (golang.org/issue/33384),
+       // so we have no choice but to ignore SIGPIPE.
+       if GOOS == "darwin" && sig == _SIGPIPE {
+               return true
+       }
+
        // If there is no handler to forward to, no need to forward.
        if fwdFn == _SIG_DFL {
                return false