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