]>
Commit | Line | Data |
---|---|---|
9cc50fc6 | 1 | #![cfg_attr(test, allow(dead_code))] |
1a4d82fc | 2 | |
60c5eb7d | 3 | use self::imp::{drop_handler, make_handler}; |
1a4d82fc | 4 | |
9cc50fc6 SL |
5 | pub use self::imp::cleanup; |
6 | pub use self::imp::init; | |
1a4d82fc JJ |
7 | |
8 | pub struct Handler { | |
60c5eb7d | 9 | _data: *mut libc::c_void, |
1a4d82fc JJ |
10 | } |
11 | ||
12 | impl Handler { | |
13 | pub unsafe fn new() -> Handler { | |
14 | make_handler() | |
15 | } | |
74b04a01 XL |
16 | |
17 | fn null() -> Handler { | |
18 | Handler { _data: crate::ptr::null_mut() } | |
19 | } | |
1a4d82fc JJ |
20 | } |
21 | ||
22 | impl Drop for Handler { | |
23 | fn drop(&mut self) { | |
24 | unsafe { | |
25 | drop_handler(self); | |
26 | } | |
27 | } | |
28 | } | |
29 | ||
60c5eb7d XL |
30 | #[cfg(any( |
31 | target_os = "linux", | |
32 | target_os = "macos", | |
33 | target_os = "dragonfly", | |
34 | target_os = "freebsd", | |
35 | target_os = "solaris", | |
36 | all(target_os = "netbsd", not(target_vendor = "rumprun")), | |
37 | target_os = "openbsd" | |
38 | ))] | |
1a4d82fc | 39 | mod imp { |
1a4d82fc | 40 | use super::Handler; |
532ac7d7 XL |
41 | use crate::mem; |
42 | use crate::ptr; | |
43 | ||
e9174d1e | 44 | use libc::MAP_FAILED; |
60c5eb7d XL |
45 | use libc::{mmap, munmap}; |
46 | use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; | |
47 | use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE}; | |
48 | use libc::{MAP_ANON, MAP_PRIVATE, PROT_READ, PROT_WRITE, SIGSEGV}; | |
1a4d82fc | 49 | |
532ac7d7 | 50 | use crate::sys_common::thread_info; |
1a4d82fc | 51 | |
92a42be0 | 52 | #[cfg(any(target_os = "linux", target_os = "android"))] |
9cc50fc6 | 53 | unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { |
92a42be0 SL |
54 | #[repr(C)] |
55 | struct siginfo_t { | |
a7813a04 | 56 | a: [libc::c_int; 3], // si_signo, si_errno, si_code |
92a42be0 SL |
57 | si_addr: *mut libc::c_void, |
58 | } | |
59 | ||
9cc50fc6 | 60 | (*(info as *const siginfo_t)).si_addr as usize |
92a42be0 SL |
61 | } |
62 | ||
63 | #[cfg(not(any(target_os = "linux", target_os = "android")))] | |
9cc50fc6 SL |
64 | unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { |
65 | (*info).si_addr as usize | |
92a42be0 SL |
66 | } |
67 | ||
e9174d1e SL |
68 | // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages |
69 | // (unmapped pages) at the end of every thread's stack, so if a thread ends | |
70 | // up running into the guard page it'll trigger this handler. We want to | |
71 | // detect these cases and print out a helpful error saying that the stack | |
72 | // has overflowed. All other signals, however, should go back to what they | |
73 | // were originally supposed to do. | |
74 | // | |
75 | // This handler currently exists purely to print an informative message | |
7453a54e SL |
76 | // whenever a thread overflows its stack. We then abort to exit and |
77 | // indicate a crash, but to avoid a misleading SIGSEGV that might lead | |
78 | // users to believe that unsafe code has accessed an invalid pointer; the | |
79 | // SIGSEGV encountered when overflowing the stack is expected and | |
80 | // well-defined. | |
e9174d1e | 81 | // |
7453a54e SL |
82 | // If this is not a stack overflow, the handler un-registers itself and |
83 | // then returns (to allow the original signal to be delivered again). | |
84 | // Returning from this kind of signal handler is technically not defined | |
85 | // to work when reading the POSIX spec strictly, but in practice it turns | |
86 | // out many large systems and all implementations allow returning from a | |
87 | // signal handler to work. For a more detailed explanation see the | |
88 | // comments on #26458. | |
60c5eb7d XL |
89 | unsafe extern "C" fn signal_handler( |
90 | signum: libc::c_int, | |
91 | info: *mut libc::siginfo_t, | |
92 | _data: *mut libc::c_void, | |
93 | ) { | |
532ac7d7 | 94 | use crate::sys_common::util::report_overflow; |
9cc50fc6 | 95 | |
2c00a5a8 | 96 | let guard = thread_info::stack_guard().unwrap_or(0..0); |
9cc50fc6 | 97 | let addr = siginfo_si_addr(info); |
1a4d82fc | 98 | |
e9174d1e | 99 | // If the faulting address is within the guard page, then we print a |
7453a54e | 100 | // message saying so and abort. |
2c00a5a8 | 101 | if guard.start <= addr && addr < guard.end { |
e9174d1e | 102 | report_overflow(); |
7453a54e SL |
103 | rtabort!("stack overflow"); |
104 | } else { | |
105 | // Unregister ourselves by reverting back to the default behavior. | |
106 | let mut action: sigaction = mem::zeroed(); | |
107 | action.sa_sigaction = SIG_DFL; | |
108 | sigaction(signum, &action, ptr::null_mut()); | |
109 | ||
110 | // See comment above for why this function returns. | |
1a4d82fc | 111 | } |
1a4d82fc JJ |
112 | } |
113 | ||
e9174d1e | 114 | static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut(); |
74b04a01 | 115 | static mut NEED_ALTSTACK: bool = false; |
1a4d82fc JJ |
116 | |
117 | pub unsafe fn init() { | |
1a4d82fc | 118 | let mut action: sigaction = mem::zeroed(); |
74b04a01 XL |
119 | for &signal in &[SIGSEGV, SIGBUS] { |
120 | sigaction(signal, ptr::null_mut(), &mut action); | |
121 | // Configure our signal handler if one is not already set. | |
122 | if action.sa_sigaction == SIG_DFL { | |
123 | action.sa_flags = SA_SIGINFO | SA_ONSTACK; | |
124 | action.sa_sigaction = signal_handler as sighandler_t; | |
125 | sigaction(signal, &action, ptr::null_mut()); | |
126 | NEED_ALTSTACK = true; | |
127 | } | |
128 | } | |
1a4d82fc JJ |
129 | |
130 | let handler = make_handler(); | |
131 | MAIN_ALTSTACK = handler._data; | |
132 | mem::forget(handler); | |
133 | } | |
134 | ||
135 | pub unsafe fn cleanup() { | |
136 | Handler { _data: MAIN_ALTSTACK }; | |
137 | } | |
138 | ||
7453a54e | 139 | unsafe fn get_stackp() -> *mut libc::c_void { |
60c5eb7d XL |
140 | let stackp = |
141 | mmap(ptr::null_mut(), SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); | |
7453a54e | 142 | if stackp == MAP_FAILED { |
1a4d82fc JJ |
143 | panic!("failed to allocate an alternative stack"); |
144 | } | |
7453a54e SL |
145 | stackp |
146 | } | |
1a4d82fc | 147 | |
60c5eb7d XL |
148 | #[cfg(any( |
149 | target_os = "linux", | |
150 | target_os = "macos", | |
151 | target_os = "freebsd", | |
152 | target_os = "netbsd", | |
153 | target_os = "openbsd", | |
154 | target_os = "solaris" | |
155 | ))] | |
7453a54e SL |
156 | unsafe fn get_stack() -> libc::stack_t { |
157 | libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ } | |
158 | } | |
1a4d82fc | 159 | |
532ac7d7 | 160 | #[cfg(target_os = "dragonfly")] |
7453a54e SL |
161 | unsafe fn get_stack() -> libc::stack_t { |
162 | libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ } | |
163 | } | |
1a4d82fc | 164 | |
7453a54e | 165 | pub unsafe fn make_handler() -> Handler { |
74b04a01 XL |
166 | if !NEED_ALTSTACK { |
167 | return Handler::null(); | |
168 | } | |
7453a54e SL |
169 | let mut stack = mem::zeroed(); |
170 | sigaltstack(ptr::null(), &mut stack); | |
171 | // Configure alternate signal stack, if one is not already set. | |
172 | if stack.ss_flags & SS_DISABLE != 0 { | |
173 | stack = get_stack(); | |
174 | sigaltstack(&stack, ptr::null_mut()); | |
175 | Handler { _data: stack.ss_sp as *mut libc::c_void } | |
176 | } else { | |
74b04a01 | 177 | Handler::null() |
7453a54e | 178 | } |
1a4d82fc JJ |
179 | } |
180 | ||
181 | pub unsafe fn drop_handler(handler: &mut Handler) { | |
7453a54e | 182 | if !handler._data.is_null() { |
60c5eb7d | 183 | let stack = libc::stack_t { |
7453a54e SL |
184 | ss_sp: ptr::null_mut(), |
185 | ss_flags: SS_DISABLE, | |
cc61c64b | 186 | // Workaround for bug in macOS implementation of sigaltstack |
7453a54e SL |
187 | // UNIX2003 which returns ENOMEM when disabling a stack while |
188 | // passing ss_size smaller than MINSIGSTKSZ. According to POSIX | |
189 | // both ss_sp and ss_size should be ignored in this case. | |
190 | ss_size: SIGSTKSZ, | |
191 | }; | |
192 | sigaltstack(&stack, ptr::null_mut()); | |
193 | munmap(handler._data, SIGSTKSZ); | |
194 | } | |
1a4d82fc | 195 | } |
1a4d82fc JJ |
196 | } |
197 | ||
60c5eb7d XL |
198 | #[cfg(not(any( |
199 | target_os = "linux", | |
200 | target_os = "macos", | |
201 | target_os = "dragonfly", | |
202 | target_os = "freebsd", | |
203 | target_os = "solaris", | |
204 | all(target_os = "netbsd", not(target_vendor = "rumprun")), | |
205 | target_os = "openbsd" | |
206 | )))] | |
1a4d82fc | 207 | mod imp { |
60c5eb7d | 208 | pub unsafe fn init() {} |
1a4d82fc | 209 | |
60c5eb7d | 210 | pub unsafe fn cleanup() {} |
1a4d82fc JJ |
211 | |
212 | pub unsafe fn make_handler() -> super::Handler { | |
74b04a01 | 213 | super::Handler::null() |
1a4d82fc JJ |
214 | } |
215 | ||
60c5eb7d | 216 | pub unsafe fn drop_handler(_handler: &mut super::Handler) {} |
1a4d82fc | 217 | } |