]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/cgo: use libc for sigaction syscalls when possible
authorBryan C. Mills <bcmills@google.com>
Wed, 9 Nov 2016 20:28:24 +0000 (15:28 -0500)
committerIan Lance Taylor <iant@golang.org>
Wed, 16 Nov 2016 05:38:38 +0000 (05:38 +0000)
This ensures that runtime's signal handlers pass through the TSAN and
MSAN libc interceptors and subsequent calls to the intercepted
sigaction function from C will correctly see them.

Fixes #17753.

Change-Id: I9798bb50291a4b8fa20caa39c02a4465ec40bb8d
Reviewed-on: https://go-review.googlesource.com/33142
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

misc/cgo/testsanitizers/test.bash
misc/cgo/testsanitizers/tsan8.go [new file with mode: 0644]
src/runtime/cgo/gcc_sigaction.c [new file with mode: 0644]
src/runtime/cgo/sigaction.go [new file with mode: 0644]
src/runtime/cgo_mmap.go
src/runtime/cgo_sigaction.go [new file with mode: 0644]
src/runtime/msan_amd64.s
src/runtime/os_linux.go
src/runtime/sigaction_linux.go [new file with mode: 0644]
src/runtime/sys_linux_amd64.s

index abbfb27d4b771c680389c1160b999ef1aefb298a..01cce956b8fccb02a20cefde06fedfcee8bce895 100755 (executable)
@@ -144,6 +144,7 @@ if test "$tsan" = "yes"; then
     testtsan tsan2.go
     testtsan tsan3.go
     testtsan tsan4.go
+    testtsan tsan8.go
 
     # These tests are only reliable using clang or GCC version 7 or later.
     # Otherwise runtime/cgo/libcgo.h can't tell whether TSAN is in use.
diff --git a/misc/cgo/testsanitizers/tsan8.go b/misc/cgo/testsanitizers/tsan8.go
new file mode 100644 (file)
index 0000000..88d82a6
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright 2016 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 main
+
+// This program failed when run under the C/C++ ThreadSanitizer.  The TSAN
+// sigaction function interceptor returned SIG_DFL instead of the Go runtime's
+// handler in registerSegvForwarder.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct sigaction prev_sa;
+
+void forwardSignal(int signo, siginfo_t *info, void *context) {
+       // One of sa_sigaction and/or sa_handler
+       if ((prev_sa.sa_flags&SA_SIGINFO) != 0) {
+               prev_sa.sa_sigaction(signo, info, context);
+               return;
+       }
+       if (prev_sa.sa_handler != SIG_IGN && prev_sa.sa_handler != SIG_DFL) {
+               prev_sa.sa_handler(signo);
+               return;
+       }
+
+       fprintf(stderr, "No Go handler to forward to!\n");
+       abort();
+}
+
+void registerSegvFowarder() {
+       struct sigaction sa;
+       memset(&sa, 0, sizeof(sa));
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
+       sa.sa_sigaction = forwardSignal;
+
+       if (sigaction(SIGSEGV, &sa, &prev_sa) != 0) {
+               perror("failed to register SEGV forwarder");
+               exit(EXIT_FAILURE);
+       }
+}
+*/
+import "C"
+
+func main() {
+       C.registerSegvFowarder()
+
+       defer func() {
+               recover()
+       }()
+       var nilp *int
+       *nilp = 42
+}
diff --git a/src/runtime/cgo/gcc_sigaction.c b/src/runtime/cgo/gcc_sigaction.c
new file mode 100644 (file)
index 0000000..aab1337
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright 2016 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.
+
+// +build linux,amd64
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <signal.h>
+
+// go_sigaction_t is a C version of the sigactiont struct from
+// defs_linux_amd64.go.  This definition — and its conversion to and from struct
+// sigaction — are specific to linux/amd64.
+typedef struct {
+       uintptr_t handler;
+       uint64_t flags;
+       uintptr_t restorer;
+       uint64_t mask;
+} go_sigaction_t;
+
+int32_t
+x_cgo_sigaction(intptr_t signum, const go_sigaction_t *goact, go_sigaction_t *oldgoact) {
+       int32_t ret;
+       struct sigaction act;
+       struct sigaction oldact;
+       int i;
+
+       if (goact) {
+               if (goact->flags & SA_SIGINFO) {
+                       act.sa_sigaction = (void(*)(int, siginfo_t*, void*))(goact->handler);
+               } else {
+                       act.sa_handler = (void(*)(int))(goact->handler);
+               }
+               sigemptyset(&act.sa_mask);
+               for (i = 0; i < 8 * sizeof(goact->mask); i++) {
+                       if (goact->mask & ((uint64_t)(1)<<i)) {
+                               sigaddset(&act.sa_mask, i+1);
+                       }
+               }
+               act.sa_flags = goact->flags;
+       }
+
+       ret = sigaction(signum, goact ? &act : NULL, oldgoact ? &oldact : NULL);
+       if (ret == -1) {
+               /* This is what the Go code expects on failure. */
+               return errno;
+       }
+
+       if (oldgoact) {
+               if (oldact.sa_flags & SA_SIGINFO) {
+                       oldgoact->handler = (uintptr_t)(oldact.sa_sigaction);
+               } else {
+                       oldgoact->handler = (uintptr_t)(oldact.sa_handler);
+               }
+               oldgoact->mask = 0;
+               for (i = 0; i < 8 * sizeof(oldgoact->mask); i++) {
+                       if (sigismember(&act.sa_mask, i+1) == 1) {
+                               oldgoact->mask |= (uint64_t)(1)<<i;
+                       }
+               }
+               oldgoact->flags = act.sa_flags;
+       }
+
+       return ret;
+}
diff --git a/src/runtime/cgo/sigaction.go b/src/runtime/cgo/sigaction.go
new file mode 100644 (file)
index 0000000..30d3f14
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 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.
+
+// +build linux,amd64
+
+package cgo
+
+// Import "unsafe" because we use go:linkname.
+import _ "unsafe"
+
+// When using cgo, call the C library for sigaction, so that we call into
+// any sanitizer interceptors. This supports using the memory
+// sanitizer with Go programs. The memory sanitizer only applies to
+// C/C++ code; this permits that code to see the Go runtime's existing signal
+// handlers when registering new signal handlers for the process.
+
+//go:cgo_import_static x_cgo_sigaction
+//go:linkname x_cgo_sigaction x_cgo_sigaction
+//go:linkname _cgo_sigaction _cgo_sigaction
+var x_cgo_sigaction byte
+var _cgo_sigaction = &x_cgo_sigaction
index a23cc79b7e350e36f5ac99765435f54643b9b02b..5a2a1a2c3704b3750437f72ad2b1f0e5d9777e28 100644 (file)
@@ -35,7 +35,6 @@ func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) uns
 // sysMmap calls the mmap system call. It is implemented in assembly.
 func sysMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) unsafe.Pointer
 
-// cgoMmap calls the mmap function in the runtime/cgo package on the
 // callCgoMmap calls the mmap function in the runtime/cgo package
 // using the GCC calling convention. It is implemented in assembly.
 func callCgoMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) uintptr
diff --git a/src/runtime/cgo_sigaction.go b/src/runtime/cgo_sigaction.go
new file mode 100644 (file)
index 0000000..4da2f40
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright 2016 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.
+
+// Support for memory sanitizer. See runtime/cgo/sigaction.go.
+
+// +build linux,amd64
+
+package runtime
+
+import "unsafe"
+
+// _cgo_sigaction is filled in by runtime/cgo when it is linked into the
+// program, so it is only non-nil when using cgo.
+//go:linkname _cgo_sigaction _cgo_sigaction
+var _cgo_sigaction unsafe.Pointer
+
+//go:nosplit
+//go:nowritebarrierrec
+func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 {
+       // The runtime package is explicitly blacklisted from sanitizer
+       // instrumentation in racewalk.go, but we might be calling into instrumented C
+       // functions here — so we need the pointer parameters to be properly marked.
+       //
+       // Mark the input as having been written before the call and the output as
+       // read after.
+       if msanenabled && new != nil {
+               msanwrite(unsafe.Pointer(new), unsafe.Sizeof(*new))
+       }
+
+       var ret int32
+
+       if _cgo_sigaction == nil {
+               ret = sysSigaction(sig, new, old, size)
+       } else {
+               // We need to call _cgo_sigaction, which means we need a big enough stack
+               // for C.  To complicate matters, we may be in libpreinit (before the
+               // runtime has been initialized) or in an asynchronous signal handler (with
+               // the current thread in transition between goroutines, or with the g0
+               // system stack already in use).
+
+               g := getg()
+               sp := uintptr(unsafe.Pointer(&sig))
+               switch {
+               case g == nil:
+                       // No g: we're on a C stack or a signal stack.
+                       ret = callCgoSigaction(sig, new, old)
+               case sp < g.stack.lo || sp >= g.stack.hi:
+                       // We're no longer on g's stack, so we must be handling a signal.  It's
+                       // possible that we interrupted the thread during a transition between g
+                       // and g0, so we should stay on the current stack to avoid corrupting g0.
+                       ret = callCgoSigaction(sig, new, old)
+               default:
+                       // We're running on g's stack, so either we're not in a signal handler or
+                       // the signal handler has set the correct g.  If we're on gsignal or g0,
+                       // systemstack will make the call directly; otherwise, it will switch to
+                       // g0 to ensure we have enough room to call a libc function.
+                       //
+                       // The function literal that we pass to systemstack is not nosplit, but
+                       // that's ok: we'll be running on a fresh, clean system stack so the stack
+                       // check will always succeed anyway.
+                       systemstack(func() {
+                               ret = callCgoSigaction(sig, new, old)
+                       })
+               }
+
+               const EINVAL = 22
+               if ret == EINVAL {
+                       // libc reserves certain signals — normally 32-33 — for pthreads, and
+                       // returns EINVAL for sigaction calls on those signals.  If we get EINVAL,
+                       // fall back to making the syscall directly.
+                       ret = sysSigaction(sig, new, old, size)
+               }
+       }
+
+       if msanenabled && old != nil && ret == 0 {
+               msanread(unsafe.Pointer(old), unsafe.Sizeof(*old))
+       }
+       return ret
+}
+
+// sysSigaction calls the rt_sigaction system call. It is implemented in assembly.
+//go:noescape
+func sysSigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
+
+// callCgoSigaction calls the sigaction function in the runtime/cgo package
+// using the GCC calling convention. It is implemented in assembly.
+//go:noescape
+func callCgoSigaction(sig uintptr, new, old *sigactiont) int32
index 9c59eece84b1c60cfdcaf7878c082a6af8dff2b9..cbe739df53a66d6e29f97c55958ac2f0c988ed28 100644 (file)
@@ -62,12 +62,16 @@ TEXT        runtime·msanfree(SB), NOSPLIT, $0-16
 TEXT   msancall<>(SB), NOSPLIT, $0-0
        get_tls(R12)
        MOVQ    g(R12), R14
+       MOVQ    SP, R12         // callee-saved, preserved across the CALL
+       CMPQ    R14, $0
+       JE      call    // no g; still on a system stack
+
        MOVQ    g_m(R14), R13
        // Switch to g0 stack.
-       MOVQ    SP, R12         // callee-saved, preserved across the CALL
        MOVQ    m_g0(R13), R10
        CMPQ    R10, R14
        JE      call    // already on g0
+
        MOVQ    (g_sched+gobuf_sp)(R10), SP
 call:
        ANDQ    $~15, SP        // alignment for gcc ABI
index 353522f69f2d89ff09e7af3767ee309fff9f73d2..72d17f549a4707e676e7c08bac33b55129f093ac 100644 (file)
@@ -311,9 +311,6 @@ func sigreturn()
 func sigtramp(sig uint32, info *siginfo, ctx unsafe.Pointer)
 func cgoSigtramp()
 
-//go:noescape
-func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
-
 //go:noescape
 func sigaltstack(new, old *stackt)
 
diff --git a/src/runtime/sigaction_linux.go b/src/runtime/sigaction_linux.go
new file mode 100644 (file)
index 0000000..0b2afb0
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2016 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.
+
+// +build !amd64
+
+package runtime
+
+// rt_sigaction calls the rt_sigaction system call. It is implemented in assembly.
+//go:noescape
+func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
index 7f88e1db23e78ef1c441cf8adc0efbb946636491..832b98b6740b50fa282ace31def2d839929b5c07 100644 (file)
@@ -208,7 +208,7 @@ TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-28
        MOVL    $0xf1, 0xf1  // crash
        RET
 
-TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
+TEXT runtime·sysSigaction(SB),NOSPLIT,$0-36
        MOVQ    sig+0(FP), DI
        MOVQ    new+8(FP), SI
        MOVQ    old+16(FP), DX
@@ -218,6 +218,19 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
        MOVL    AX, ret+32(FP)
        RET
 
+// Call the function stored in _cgo_sigaction using the GCC calling convention.
+TEXT runtime·callCgoSigaction(SB),NOSPLIT,$16
+       MOVQ    sig+0(FP), DI
+       MOVQ    new+8(FP), SI
+       MOVQ    old+16(FP), DX
+       MOVQ    _cgo_sigaction(SB), AX
+       MOVQ    SP, BX  // callee-saved
+       ANDQ    $~15, SP        // alignment as per amd64 psABI
+       CALL    AX
+       MOVQ    BX, SP
+       MOVL    AX, ret+24(FP)
+       RET
+
 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
        MOVQ    fn+0(FP),    AX
        MOVL    sig+8(FP),   DI