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