]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: run libc SIGSETXID and SIGCANCEL handlers on signal stack
authorAustin Clements <austin@google.com>
Fri, 19 Dec 2014 21:16:17 +0000 (16:16 -0500)
committerAustin Clements <austin@google.com>
Tue, 23 Dec 2014 01:33:36 +0000 (01:33 +0000)
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>
17 files changed:
misc/cgo/test/cgo_linux_test.go
misc/cgo/test/issue9400/asm_386.s [new file with mode: 0644]
misc/cgo/test/issue9400/asm_amd64x.s [new file with mode: 0644]
misc/cgo/test/issue9400/asm_arm.s [new file with mode: 0644]
misc/cgo/test/issue9400/asm_ppc64x.s [new file with mode: 0644]
misc/cgo/test/issue9400/stubs.go [new file with mode: 0644]
misc/cgo/test/issue9400_linux.go [new file with mode: 0644]
src/runtime/os1_darwin.go
src/runtime/os1_dragonfly.go
src/runtime/os1_freebsd.go
src/runtime/os1_linux.go
src/runtime/os1_netbsd.go
src/runtime/os1_openbsd.go
src/runtime/os3_solaris.go
src/runtime/runtime2.go
src/runtime/signal1_unix.go
src/runtime/signal_linux.go

index 4fe0db1b2b9c3bb2594e0d8909736a2ff845151e..6e1d1065f63bbcca821c79267743f1a1c718f2c6 100644 (file)
@@ -9,3 +9,4 @@ import "testing"
 func TestSetgid(t *testing.T)  { testSetgid(t) }
 func Test6997(t *testing.T)    { test6997(t) }
 func TestBuildID(t *testing.T) { testBuildID(t) }
+func Test9400(t *testing.T)    { test9400(t) }
diff --git a/misc/cgo/test/issue9400/asm_386.s b/misc/cgo/test/issue9400/asm_386.s
new file mode 100644 (file)
index 0000000..b277fa1
--- /dev/null
@@ -0,0 +1,20 @@
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0
+       // Rewind stack pointer so anything that happens on the stack
+       // will clobber the test pattern created by the caller
+       ADDL    $(1024 * 8), SP
+
+       // Ask signaller to setgid
+       MOVL    $1, ·Baton(SB)
+
+       // Wait for setgid completion
+loop:
+       PAUSE
+       MOVL    ·Baton(SB), AX
+       CMPL    AX, $0
+       JNE     loop
+
+       // Restore stack
+       SUBL    $(1024 * 8), SP
+       RET
diff --git a/misc/cgo/test/issue9400/asm_amd64x.s b/misc/cgo/test/issue9400/asm_amd64x.s
new file mode 100644 (file)
index 0000000..2c97e13
--- /dev/null
@@ -0,0 +1,22 @@
+// +build amd64 amd64p32
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0
+       // Rewind stack pointer so anything that happens on the stack
+       // will clobber the test pattern created by the caller
+       ADDQ    $(1024 * 8), SP
+
+       // Ask signaller to setgid
+       MOVL    $1, ·Baton(SB)
+
+       // Wait for setgid completion
+loop:
+       PAUSE
+       MOVL    ·Baton(SB), AX
+       CMPL    AX, $0
+       JNE     loop
+
+       // Restore stack
+       SUBQ    $(1024 * 8), SP
+       RET
diff --git a/misc/cgo/test/issue9400/asm_arm.s b/misc/cgo/test/issue9400/asm_arm.s
new file mode 100644 (file)
index 0000000..68e3a3b
--- /dev/null
@@ -0,0 +1,33 @@
+#include "textflag.h"
+
+TEXT cas<>(SB),NOSPLIT,$0
+       MOVW    $0xffff0fc0, PC
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT,$-4-0
+       // Save link register
+       MOVW    R14, R4
+
+       // Rewind stack pointer so anything that happens on the stack
+       // will clobber the test pattern created by the caller
+       ADD     $(1024 * 8), R13
+
+       // Ask signaller to setgid
+       MOVW    $·Baton(SB), R2
+storeloop:
+       MOVW    0(R2), R0
+       MOVW    $1, R1
+       BL      cas<>(SB)
+       BCC     storeloop
+
+       // Wait for setgid completion
+loop:
+       MOVW    $0, R0
+       MOVW    $0, R1
+       BL      cas<>(SB)
+       BCC     loop
+
+       // Restore stack
+       SUB     $(1024 * 8), R13
+
+       MOVW    R4, R14
+       RET
diff --git a/misc/cgo/test/issue9400/asm_ppc64x.s b/misc/cgo/test/issue9400/asm_ppc64x.s
new file mode 100644 (file)
index 0000000..0aaa10c
--- /dev/null
@@ -0,0 +1,27 @@
+// +build ppc64 ppc64le
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT,$-8-0
+       // Rewind stack pointer so anything that happens on the stack
+       // will clobber the test pattern created by the caller
+       ADD     $(1024 * 8), R1
+
+       // Ask signaller to setgid
+       MOVW    $1, R3
+       SYNC
+       MOVW    R3, ·Baton(SB)
+
+       // Wait for setgid completion
+loop:
+       SYNC
+       MOVW    ·Baton(SB), R3
+       CMP     R3, $0
+       // Hint that we're in a spin loop
+       OR      R1, R1, R1
+       BNE     loop
+       ISYNC
+
+       // Restore stack
+       SUB     $(1024 * 8), R1
+       RET
diff --git a/misc/cgo/test/issue9400/stubs.go b/misc/cgo/test/issue9400/stubs.go
new file mode 100644 (file)
index 0000000..1dd8ccd
--- /dev/null
@@ -0,0 +1,9 @@
+// Copyright 2014 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 issue9400
+
+var Baton int32
+
+func RewindAndSetgid()
diff --git a/misc/cgo/test/issue9400_linux.go b/misc/cgo/test/issue9400_linux.go
new file mode 100644 (file)
index 0000000..d2386b8
--- /dev/null
@@ -0,0 +1,58 @@
+// Copyright 2014 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.
+
+// Test that SIGSETXID runs on signal stack, since it's likely to
+// overflow if it runs on the Go stack.
+
+package cgotest
+
+/*
+#include <sys/types.h>
+#include <unistd.h>
+*/
+import "C"
+
+import (
+       "runtime"
+       "sync/atomic"
+       "testing"
+
+       "./issue9400"
+)
+
+func test9400(t *testing.T) {
+       // We synchronize through a shared variable, so we need two procs
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
+
+       // Start signaller
+       atomic.StoreInt32(&issue9400.Baton, 0)
+       go func() {
+               // Wait for RewindAndSetgid
+               for atomic.LoadInt32(&issue9400.Baton) == 0 {
+                       runtime.Gosched()
+               }
+               // Broadcast SIGSETXID
+               runtime.LockOSThread()
+               C.setgid(0)
+               // Indicate that signalling is done
+               atomic.StoreInt32(&issue9400.Baton, 0)
+       }()
+
+       // Grow the stack and put down a test pattern
+       const pattern = 0x123456789abcdef
+       var big [1024]uint64 // len must match assmebly
+       for i := range big {
+               big[i] = pattern
+       }
+
+       // Temporarily rewind the stack and trigger SIGSETXID
+       issue9400.RewindAndSetgid()
+
+       // Check test pattern
+       for i := range big {
+               if big[i] != pattern {
+                       t.Fatalf("entry %d of test pattern is wrong; %#x != %#x", i, big[i], pattern)
+               }
+       }
+}
index 12642aa121ab723e3b5de1965cdff09a57172bf7..984b88161ccb4401f073cd504e7e3d47f90c9818 100644 (file)
@@ -394,6 +394,10 @@ func setsig(i int32, fn uintptr, restart bool) {
        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))
index d02e925ec51badc9dbadabfb314a9b60942e88fb..0d241bde79190dc5712d0bbc768ca8fd0be6531c 100644 (file)
@@ -189,6 +189,10 @@ func setsig(i int32, fn uintptr, restart bool) {
        sigaction(i, &sa, nil)
 }
 
+func setsigstack(i int32) {
+       gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
        var sa sigactiont
        sigaction(i, nil, &sa)
index 80e45324391716b1d839c19d42d792d78debbc48..83e98f45e47d7cdae9056c410e1a9dfdf27ad465 100644 (file)
@@ -190,6 +190,11 @@ func setsig(i int32, fn uintptr, restart bool) {
        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)
index 2e12d74f442d282c6edfb276586a0c0d9075a1a0..0174856914e8f77f375c213abb278553672a27f0 100644 (file)
@@ -246,6 +246,20 @@ func setsig(i int32, fn uintptr, restart bool) {
        }
 }
 
+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
 
index b5068629ba340b8465ea2987f8c29bd98b5612d5..f4de9887077ea5cdf87099d1efa8009608c3de50 100644 (file)
@@ -233,6 +233,10 @@ func setsig(i int32, fn uintptr, restart bool) {
        sigaction(i, &sa, nil)
 }
 
+func setsigstack(i int32) {
+       gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
        var sa sigactiont
        sigaction(i, nil, &sa)
index b1a16d582b958053fd5b4934d1b7554e4e707a29..07a9751fe0d4465516e418a32c67d140142133d5 100644 (file)
@@ -203,6 +203,10 @@ func setsig(i int32, fn uintptr, restart bool) {
        sigaction(i, &sa, nil)
 }
 
+func setsigstack(i int32) {
+       gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
        var sa sigactiont
        sigaction(i, nil, &sa)
index 6ccbbe29eea53d52ff71d67c20f6bca924527e3d..72db958f9930938f2fabe90d7637e1b44c05531a 100644 (file)
@@ -252,6 +252,10 @@ func setsig(i int32, fn uintptr, restart bool) {
        sigaction(i, &sa, nil)
 }
 
+func setsigstack(i int32) {
+       gothrow("setsigstack")
+}
+
 func getsig(i int32) uintptr {
        var sa sigactiont
        sigaction(i, nil, &sa)
index 4d42153abbd2f04560068831960287fd28163c89..3b7db1e412ecf157fa9a9b29792d75377f3c8185 100644 (file)
@@ -372,6 +372,7 @@ const (
        _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
index 25f01e056e62c057449c523a47873cc26f6a8a28..9613e0ae335384c5083536064c06e083e901684f 100644 (file)
@@ -37,6 +37,11 @@ func initsig() {
                        }
                }
 
+               if t.flags&_SigSetStack != 0 {
+                       setsigstack(i)
+                       continue
+               }
+
                t.flags |= _SigHandling
                setsig(i, funcPC(sighandler), true)
        }
index 1c3d6872b3c7872024055393854d3650c32da72a..c71e619b1ec3fc924d8ed40be585cbe5c426b43b 100644 (file)
@@ -42,8 +42,8 @@ var sigtable = [...]sigTabT{
        /* 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"},