diff options
-rw-r--r-- | src/runtime/crash_cgo_test.go | 13 | ||||
-rw-r--r-- | src/runtime/os3_plan9.go | 3 | ||||
-rw-r--r-- | src/runtime/os_nacl.go | 3 | ||||
-rw-r--r-- | src/runtime/runtime2.go | 9 | ||||
-rw-r--r-- | src/runtime/signal_unix.go | 10 | ||||
-rw-r--r-- | src/runtime/signal_windows.go | 3 | ||||
-rw-r--r-- | src/runtime/testdata/testprogcgo/sigstack.go | 95 |
7 files changed, 131 insertions, 5 deletions
diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index a5cbbad69b..57fe796503 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -411,3 +411,16 @@ func TestCgoNumGoroutine(t *testing.T) { t.Errorf("expected %q got %v", want, got) } } + +func TestSigStackSwapping(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skip("no sigaltstack on %s", runtime.GOOS) + } + t.Parallel() + got := runTestProg(t, "testprogcgo", "SigStack") + want := "OK\n" + if got != want { + t.Errorf("expected %q got %v", want, got) + } +} diff --git a/src/runtime/os3_plan9.go b/src/runtime/os3_plan9.go index 5d4b5a6698..3b65a2c9ba 100644 --- a/src/runtime/os3_plan9.go +++ b/src/runtime/os3_plan9.go @@ -153,3 +153,6 @@ func setThreadCPUProfiler(hz int32) { // TODO: Enable profiling interrupts. getg().m.profilehz = hz } + +// gsignalStack is unused on Plan 9. +type gsignalStack struct{} diff --git a/src/runtime/os_nacl.go b/src/runtime/os_nacl.go index 18e6ce6232..1284832706 100644 --- a/src/runtime/os_nacl.go +++ b/src/runtime/os_nacl.go @@ -285,6 +285,9 @@ func sigenable(uint32) {} func sigignore(uint32) {} func closeonexec(int32) {} +// gsignalStack is unused on nacl. +type gsignalStack struct{} + var writelock uint32 // test-and-set spin lock for write /* diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 6871d9c68c..90364edb84 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -386,10 +386,11 @@ type m struct { divmod uint32 // div/mod denominator for arm - known to liblink // Fields not known to debuggers. - procid uint64 // for debuggers, but offset not hard-coded - gsignal *g // signal-handling g - sigmask sigset // storage for saved signal mask - tls [6]uintptr // thread-local storage (for x86 extern register) + procid uint64 // for debuggers, but offset not hard-coded + gsignal *g // signal-handling g + goSigStack gsignalStack // Go-allocated signal handling stack + sigmask sigset // storage for saved signal mask + tls [6]uintptr // thread-local storage (for x86 extern register) mstartfn func() curg *g // current running goroutine caughtsig guintptr // goroutine running during fatal signal diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go index 539b165ba1..a495634e69 100644 --- a/src/runtime/signal_unix.go +++ b/src/runtime/signal_unix.go @@ -702,7 +702,7 @@ func minitSignalStack() { signalstack(&_g_.m.gsignal.stack) _g_.m.newSigstack = true } else { - setGsignalStack(&st, nil) + setGsignalStack(&st, &_g_.m.goSigStack) _g_.m.newSigstack = false } } @@ -732,6 +732,14 @@ func unminitSignals() { if getg().m.newSigstack { st := stackt{ss_flags: _SS_DISABLE} sigaltstack(&st, nil) + } else { + // We got the signal stack from someone else. Restore + // the Go-allocated stack in case this M gets reused + // for another thread (e.g., it's an extram). Also, on + // Android, libc allocates a signal stack for all + // threads, so it's important to restore the Go stack + // even on Go-created threads so we can free it. + restoreGsignalStack(&getg().m.goSigStack) } } diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go index 73bd5b5cfc..8a63e58e54 100644 --- a/src/runtime/signal_windows.go +++ b/src/runtime/signal_windows.go @@ -223,3 +223,6 @@ func crash() { // It's okay to leave this empty for now: if crash returns // the ordinary exit-after-panic happens. } + +// gsignalStack is unused on Windows. +type gsignalStack struct{} diff --git a/src/runtime/testdata/testprogcgo/sigstack.go b/src/runtime/testdata/testprogcgo/sigstack.go new file mode 100644 index 0000000000..526ed4232b --- /dev/null +++ b/src/runtime/testdata/testprogcgo/sigstack.go @@ -0,0 +1,95 @@ +// Copyright 2017 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 !plan9,!windows + +// Test handling of Go-allocated signal stacks when calling from +// C-created threads with and without signal stacks. (See issue +// #22930.) + +package main + +/* +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> + +#ifndef MAP_STACK +#define MAP_STACK 0 +#endif + +extern void SigStackCallback(); + +static void* WithSigStack(void* arg __attribute__((unused))) { + // Set up an alternate system stack. + void* base = mmap(0, SIGSTKSZ, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0); + if (base == MAP_FAILED) { + perror("mmap failed"); + abort(); + } + stack_t st = {}, ost = {}; + st.ss_sp = (char*)base; + st.ss_flags = 0; + st.ss_size = SIGSTKSZ; + if (sigaltstack(&st, &ost) < 0) { + perror("sigaltstack failed"); + abort(); + } + + // Call Go. + SigStackCallback(); + + // Disable signal stack and protect it so we can detect reuse. + if (ost.ss_flags & SS_DISABLE) { + // Darwin libsystem has a bug where it checks ss_size + // even if SS_DISABLE is set. (The kernel gets it right.) + ost.ss_size = SIGSTKSZ; + } + if (sigaltstack(&ost, NULL) < 0) { + perror("sigaltstack restore failed"); + abort(); + } + mprotect(base, SIGSTKSZ, PROT_NONE); + return NULL; +} + +static void* WithoutSigStack(void* arg __attribute__((unused))) { + SigStackCallback(); + return NULL; +} + +static void DoThread(int sigstack) { + pthread_t tid; + if (sigstack) { + pthread_create(&tid, NULL, WithSigStack, NULL); + } else { + pthread_create(&tid, NULL, WithoutSigStack, NULL); + } + pthread_join(tid, NULL); +} +*/ +import "C" + +func init() { + register("SigStack", SigStack) +} + +func SigStack() { + C.DoThread(0) + C.DoThread(1) + C.DoThread(0) + C.DoThread(1) + println("OK") +} + +var BadPtr *int + +//export SigStackCallback +func SigStackCallback() { + // Cause the Go signal handler to run. + defer func() { recover() }() + *BadPtr = 42 +} |