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