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