runtime: run libc SIGSETXID and SIGCANCEL handlers on signal stack

These signals are used by glibc to broadcast setuid/setgid to all
threads and to send pthread cancellations.  Unlike other signals, the
Go runtime does not intercept these because they must invoke the libc
handlers (see issues #3871 and #6997).  However, because 1) these
signals may be issued asynchronously by a thread running C code to
another thread running Go code and 2) glibc does not set SA_ONSTACK
for its handlers, glibc's signal handler may be run on a Go stack.
Signal frames range from 1.5K on amd64 to many kilobytes on ppc64, so
this may overflow the Go stack and corrupt heap (or other stack) data.

Fix this by ensuring that these signal handlers have the SA_ONSTACK
flag (but not otherwise taking over the handler).

This has been a problem since Go 1.1, but it's likely that people
haven't encountered it because it only affects setuid/setgid and
pthread_cancel.

Fixes #9600.

Change-Id: I6cf5f5c2d3aa48998d632f61f1ddc2778dcfd300
Reviewed-on: https://go-review.googlesource.com/1887
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go
index 12642aa..984b881 100644
--- a/src/runtime/os1_darwin.go
+++ b/src/runtime/os1_darwin.go
@@ -394,6 +394,10 @@
 	sigaction(uint32(i), &sa, nil)
 }
 
+func setsigstack(i int32) {
+	gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
 	var sa sigactiont
 	memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))
diff --git a/src/runtime/os1_dragonfly.go b/src/runtime/os1_dragonfly.go
index d02e925..0d241bd 100644
--- a/src/runtime/os1_dragonfly.go
+++ b/src/runtime/os1_dragonfly.go
@@ -189,6 +189,10 @@
 	sigaction(i, &sa, nil)
 }
 
+func setsigstack(i int32) {
+	gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
diff --git a/src/runtime/os1_freebsd.go b/src/runtime/os1_freebsd.go
index 80e4532..83e98f4 100644
--- a/src/runtime/os1_freebsd.go
+++ b/src/runtime/os1_freebsd.go
@@ -190,6 +190,11 @@
 	sa.sa_handler = fn
 	sigaction(i, &sa, nil)
 }
+
+func setsigstack(i int32) {
+	gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go
index 2e12d74..0174856 100644
--- a/src/runtime/os1_linux.go
+++ b/src/runtime/os1_linux.go
@@ -246,6 +246,20 @@
 	}
 }
 
+func setsigstack(i int32) {
+	var sa sigactiont
+	if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
+		gothrow("rt_sigaction failure")
+	}
+	if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 {
+		return
+	}
+	sa.sa_flags |= _SA_ONSTACK
+	if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 {
+		gothrow("rt_sigaction failure")
+	}
+}
+
 func getsig(i int32) uintptr {
 	var sa sigactiont
 
diff --git a/src/runtime/os1_netbsd.go b/src/runtime/os1_netbsd.go
index b506862..f4de988 100644
--- a/src/runtime/os1_netbsd.go
+++ b/src/runtime/os1_netbsd.go
@@ -233,6 +233,10 @@
 	sigaction(i, &sa, nil)
 }
 
+func setsigstack(i int32) {
+	gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
diff --git a/src/runtime/os1_openbsd.go b/src/runtime/os1_openbsd.go
index b1a16d5..07a9751 100644
--- a/src/runtime/os1_openbsd.go
+++ b/src/runtime/os1_openbsd.go
@@ -203,6 +203,10 @@
 	sigaction(i, &sa, nil)
 }
 
+func setsigstack(i int32) {
+	gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index 6ccbbe2..72db958 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -252,6 +252,10 @@
 	sigaction(i, &sa, nil)
 }
 
+func setsigstack(i int32) {
+	gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 4d42153..3b7db1e 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -372,6 +372,7 @@
 	_SigHandling = 1 << 5 // our signal handler is registered
 	_SigIgnored  = 1 << 6 // the signal was ignored before we registered for it
 	_SigGoExit   = 1 << 7 // cause all runtime procs to exit (only used on Plan 9).
+	_SigSetStack = 1 << 8 // add SA_ONSTACK to libc handler
 )
 
 // Layout of in-memory per-function information prepared by linker
diff --git a/src/runtime/signal1_unix.go b/src/runtime/signal1_unix.go
index 25f01e0..9613e0a 100644
--- a/src/runtime/signal1_unix.go
+++ b/src/runtime/signal1_unix.go
@@ -37,6 +37,11 @@
 			}
 		}
 
+		if t.flags&_SigSetStack != 0 {
+			setsigstack(i)
+			continue
+		}
+
 		t.flags |= _SigHandling
 		setsig(i, funcPC(sighandler), true)
 	}
diff --git a/src/runtime/signal_linux.go b/src/runtime/signal_linux.go
index 1c3d687..c71e619 100644
--- a/src/runtime/signal_linux.go
+++ b/src/runtime/signal_linux.go
@@ -42,8 +42,8 @@
 	/* 29 */ {_SigNotify, "SIGIO: i/o now possible"},
 	/* 30 */ {_SigNotify, "SIGPWR: power failure restart"},
 	/* 31 */ {_SigNotify, "SIGSYS: bad system call"},
-	/* 32 */ {0, "signal 32"}, /* SIGCANCEL; see issue 6997 */
-	/* 33 */ {0, "signal 33"}, /* SIGSETXID; see issue 3871 */
+	/* 32 */ {_SigSetStack, "signal 32"}, /* SIGCANCEL; see issue 6997 */
+	/* 33 */ {_SigSetStack, "signal 33"}, /* SIGSETXID; see issue 3871, 9400 */
 	/* 34 */ {_SigNotify, "signal 34"},
 	/* 35 */ {_SigNotify, "signal 35"},
 	/* 36 */ {_SigNotify, "signal 36"},