]>
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. | |
9cc50fc6 | 10 | #![cfg_attr(test, allow(dead_code))] |
1a4d82fc JJ |
11 | |
12 | use libc; | |
1a4d82fc JJ |
13 | use self::imp::{make_handler, drop_handler}; |
14 | ||
9cc50fc6 SL |
15 | pub use self::imp::cleanup; |
16 | pub use self::imp::init; | |
1a4d82fc JJ |
17 | |
18 | pub struct Handler { | |
19 | _data: *mut libc::c_void | |
20 | } | |
21 | ||
22 | impl Handler { | |
23 | pub unsafe fn new() -> Handler { | |
24 | make_handler() | |
25 | } | |
26 | } | |
27 | ||
28 | impl Drop for Handler { | |
29 | fn drop(&mut self) { | |
30 | unsafe { | |
31 | drop_handler(self); | |
32 | } | |
33 | } | |
34 | } | |
35 | ||
85aaf69f SL |
36 | #[cfg(any(target_os = "linux", |
37 | target_os = "macos", | |
c34b1796 | 38 | target_os = "bitrig", |
92a42be0 SL |
39 | target_os = "dragonfly", |
40 | target_os = "freebsd", | |
b039eaaf | 41 | all(target_os = "netbsd", not(target_vendor = "rumprun")), |
85aaf69f | 42 | target_os = "openbsd"))] |
1a4d82fc | 43 | mod imp { |
1a4d82fc | 44 | use super::Handler; |
1a4d82fc JJ |
45 | use mem; |
46 | use ptr; | |
9cc50fc6 | 47 | use libc::{sigaltstack, SIGSTKSZ}; |
92a42be0 | 48 | use libc::{sigaction, SIGBUS, SIG_DFL, |
9cc50fc6 | 49 | SA_SIGINFO, SA_ONSTACK, sighandler_t}; |
1a4d82fc | 50 | use libc; |
92a42be0 | 51 | use libc::{mmap, munmap}; |
e9174d1e SL |
52 | use libc::{SIGSEGV, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON}; |
53 | use libc::MAP_FAILED; | |
1a4d82fc JJ |
54 | |
55 | use sys_common::thread_info; | |
56 | ||
57 | ||
58 | // This is initialized in init() and only read from after | |
c34b1796 | 59 | static mut PAGE_SIZE: usize = 0; |
1a4d82fc | 60 | |
92a42be0 | 61 | #[cfg(any(target_os = "linux", target_os = "android"))] |
9cc50fc6 | 62 | unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { |
92a42be0 SL |
63 | #[repr(C)] |
64 | struct siginfo_t { | |
65 | a: [libc::c_int; 3], // si_signo, si_code, si_errno, | |
66 | si_addr: *mut libc::c_void, | |
67 | } | |
68 | ||
9cc50fc6 | 69 | (*(info as *const siginfo_t)).si_addr as usize |
92a42be0 SL |
70 | } |
71 | ||
72 | #[cfg(not(any(target_os = "linux", target_os = "android")))] | |
9cc50fc6 SL |
73 | unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { |
74 | (*info).si_addr as usize | |
92a42be0 SL |
75 | } |
76 | ||
e9174d1e SL |
77 | // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages |
78 | // (unmapped pages) at the end of every thread's stack, so if a thread ends | |
79 | // up running into the guard page it'll trigger this handler. We want to | |
80 | // detect these cases and print out a helpful error saying that the stack | |
81 | // has overflowed. All other signals, however, should go back to what they | |
82 | // were originally supposed to do. | |
83 | // | |
84 | // This handler currently exists purely to print an informative message | |
85 | // whenever a thread overflows its stack. When run the handler always | |
86 | // un-registers itself after running and then returns (to allow the original | |
87 | // signal to be delivered again). By returning we're ensuring that segfaults | |
88 | // do indeed look like segfaults. | |
89 | // | |
90 | // Returning from this kind of signal handler is technically not defined to | |
91 | // work when reading the POSIX spec strictly, but in practice it turns out | |
92 | // many large systems and all implementations allow returning from a signal | |
93 | // handler to work. For a more detailed explanation see the comments on | |
94 | // #26458. | |
1a4d82fc | 95 | unsafe extern fn signal_handler(signum: libc::c_int, |
92a42be0 | 96 | info: *mut libc::siginfo_t, |
e9174d1e | 97 | _data: *mut libc::c_void) { |
9cc50fc6 SL |
98 | use sys_common::util::report_overflow; |
99 | ||
d9579d0f | 100 | let guard = thread_info::stack_guard().unwrap_or(0); |
9cc50fc6 | 101 | let addr = siginfo_si_addr(info); |
1a4d82fc | 102 | |
e9174d1e SL |
103 | // If the faulting address is within the guard page, then we print a |
104 | // message saying so. | |
105 | if guard != 0 && guard - PAGE_SIZE <= addr && addr < guard { | |
106 | report_overflow(); | |
1a4d82fc JJ |
107 | } |
108 | ||
e9174d1e SL |
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()); | |
1a4d82fc | 113 | |
e9174d1e | 114 | // See comment above for why this function returns. |
1a4d82fc JJ |
115 | } |
116 | ||
e9174d1e | 117 | static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut(); |
1a4d82fc JJ |
118 | |
119 | pub unsafe fn init() { | |
e9174d1e | 120 | PAGE_SIZE = ::sys::os::page_size(); |
1a4d82fc JJ |
121 | |
122 | let mut action: sigaction = mem::zeroed(); | |
123 | action.sa_flags = SA_SIGINFO | SA_ONSTACK; | |
124 | action.sa_sigaction = signal_handler as sighandler_t; | |
125 | sigaction(SIGSEGV, &action, ptr::null_mut()); | |
126 | sigaction(SIGBUS, &action, ptr::null_mut()); | |
127 | ||
128 | let handler = make_handler(); | |
129 | MAIN_ALTSTACK = handler._data; | |
130 | mem::forget(handler); | |
131 | } | |
132 | ||
133 | pub unsafe fn cleanup() { | |
134 | Handler { _data: MAIN_ALTSTACK }; | |
135 | } | |
136 | ||
137 | pub unsafe fn make_handler() -> Handler { | |
138 | let alt_stack = mmap(ptr::null_mut(), | |
62682a34 | 139 | SIGSTKSZ, |
1a4d82fc JJ |
140 | PROT_READ | PROT_WRITE, |
141 | MAP_PRIVATE | MAP_ANON, | |
142 | -1, | |
143 | 0); | |
144 | if alt_stack == MAP_FAILED { | |
145 | panic!("failed to allocate an alternative stack"); | |
146 | } | |
147 | ||
92a42be0 | 148 | let mut stack: libc::stack_t = mem::zeroed(); |
1a4d82fc JJ |
149 | |
150 | stack.ss_sp = alt_stack; | |
151 | stack.ss_flags = 0; | |
152 | stack.ss_size = SIGSTKSZ; | |
153 | ||
154 | sigaltstack(&stack, ptr::null_mut()); | |
155 | ||
156 | Handler { _data: alt_stack } | |
157 | } | |
158 | ||
159 | pub unsafe fn drop_handler(handler: &mut Handler) { | |
160 | munmap(handler._data, SIGSTKSZ); | |
161 | } | |
1a4d82fc JJ |
162 | } |
163 | ||
164 | #[cfg(not(any(target_os = "linux", | |
85aaf69f | 165 | target_os = "macos", |
c34b1796 | 166 | target_os = "bitrig", |
92a42be0 SL |
167 | target_os = "dragonfly", |
168 | target_os = "freebsd", | |
b039eaaf | 169 | all(target_os = "netbsd", not(target_vendor = "rumprun")), |
85aaf69f | 170 | target_os = "openbsd")))] |
1a4d82fc | 171 | mod imp { |
e9174d1e | 172 | use ptr; |
1a4d82fc JJ |
173 | |
174 | pub unsafe fn init() { | |
175 | } | |
176 | ||
177 | pub unsafe fn cleanup() { | |
178 | } | |
179 | ||
180 | pub unsafe fn make_handler() -> super::Handler { | |
e9174d1e | 181 | super::Handler { _data: ptr::null_mut() } |
1a4d82fc JJ |
182 | } |
183 | ||
184 | pub unsafe fn drop_handler(_handler: &mut super::Handler) { | |
185 | } | |
186 | } |