Repository navigation
Expand file tree
/
Copy pathsigonstack_linux.go
More file actions
117 lines (106 loc) · 2.7 KB
/
Copy pathsigonstack_linux.go
File metadata and controls
117 lines (106 loc) · 2.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// SPDX-License-Identifier: MIT
//go:build copilot_inprocess && linux
package ffihost
import (
"runtime"
"syscall"
"unsafe"
)
const (
linuxSaOnStack = 0x08000000
linuxSigDfl = 0
linuxSigIgn = 1
linuxSigChild = 17
linuxMaxSignal = 31
)
// linuxSigaction matches the kernel rt_sigaction ABI used by the Go runtime on
// Linux amd64 and arm64.
type linuxSigaction struct {
handler uintptr
flags uint64
restorer uintptr
mask uint64
}
// rearmForeignSignalHandlers re-adds the SA_ONSTACK flag to any signal handler
// installed by the native runtime that omitted it. Tokio's process-global
// SIGCHLD registration through signal-hook-registry chains Go's handler but
// replaces its flags without SA_ONSTACK. The Go runtime aborts with "non-Go code
// set up signal handler without SA_ONSTACK flag" when SIGCHLD (signal 17 on
// Linux) is delivered while a Go-managed child process is reaped.
//
// We preserve each foreign handler and merely OR in SA_ONSTACK, so Tokio's child
// watching keeps working while the Go runtime stays happy. Handlers left at
// SIG_DFL/SIG_IGN and Go's own handlers (which already carry SA_ONSTACK) are
// untouched. Best-effort: any failure is silently ignored, since the worst case
// is the pre-existing crash.
func rearmForeignSignalHandlers(_ uintptr) {
for sig := 1; sig <= linuxMaxSignal; sig++ {
rearmLinuxSignalHandler(sig)
}
}
func rearmLinuxSignalHandler(sig int) {
var action linuxSigaction
if !linuxGetSigaction(sig, &action) {
return
}
if action.handler == linuxSigDfl || action.handler == linuxSigIgn {
return
}
if action.flags&linuxSaOnStack != 0 {
return
}
action.flags |= linuxSaOnStack
linuxSetSigaction(sig, &action)
}
func protectChildProcessSignalHandler() func() {
return protectLinuxSignalHandler(linuxSigChild)
}
func protectLinuxSignalHandler(sig int) func() {
stop := make(chan struct{})
ready := make(chan struct{})
stopped := make(chan struct{})
go func() {
defer close(stopped)
rearmLinuxSignalHandler(sig)
close(ready)
for {
select {
case <-stop:
return
default:
rearmLinuxSignalHandler(sig)
runtime.Gosched()
}
}
}()
<-ready
return func() {
close(stop)
<-stopped
rearmLinuxSignalHandler(sig)
}
}
func linuxGetSigaction(signal int, action *linuxSigaction) bool {
_, _, errno := syscall.RawSyscall6(
syscall.SYS_RT_SIGACTION,
uintptr(signal),
0,
uintptr(unsafe.Pointer(action)),
unsafe.Sizeof(action.mask),
0,
0,
)
return errno == 0
}
func linuxSetSigaction(signal int, action *linuxSigaction) bool {
_, _, errno := syscall.RawSyscall6(
syscall.SYS_RT_SIGACTION,
uintptr(signal),
uintptr(unsafe.Pointer(action)),
0,
unsafe.Sizeof(action.mask),
0,
0,
)
return errno == 0
}