]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/stack_overflow.rs
Rollup merge of #84320 - jsha:details-implementors, r=Manishearth,Nemo157,GuillaumeGomez
[rust.git] / library / std / src / sys / unix / stack_overflow.rs
1 #![cfg_attr(test, allow(dead_code))]
2
3 use self::imp::{drop_handler, make_handler};
4
5 pub use self::imp::cleanup;
6 pub use self::imp::init;
7
8 pub struct Handler {
9     _data: *mut libc::c_void,
10 }
11
12 impl Handler {
13     pub unsafe fn new() -> Handler {
14         make_handler()
15     }
16
17     fn null() -> Handler {
18         Handler { _data: crate::ptr::null_mut() }
19     }
20 }
21
22 impl Drop for Handler {
23     fn drop(&mut self) {
24         unsafe {
25             drop_handler(self);
26         }
27     }
28 }
29
30 #[cfg(any(
31     target_os = "linux",
32     target_os = "macos",
33     target_os = "dragonfly",
34     target_os = "freebsd",
35     target_os = "solaris",
36     target_os = "illumos",
37     target_os = "netbsd",
38     target_os = "openbsd"
39 ))]
40 mod imp {
41     use super::Handler;
42     use crate::io;
43     use crate::mem;
44     use crate::ptr;
45
46     use libc::MAP_FAILED;
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};
50     use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV};
51
52     use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
53     use crate::sys::unix::os::page_size;
54     use crate::sys_common::thread_info;
55
56     #[cfg(any(target_os = "linux", target_os = "android"))]
57     unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
58         #[repr(C)]
59         struct siginfo_t {
60             a: [libc::c_int; 3], // si_signo, si_errno, si_code
61             si_addr: *mut libc::c_void,
62         }
63
64         (*(info as *const siginfo_t)).si_addr as usize
65     }
66
67     #[cfg(not(any(target_os = "linux", target_os = "android")))]
68     unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
69         (*info).si_addr as usize
70     }
71
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
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.
85     //
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.
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     ) {
98         use crate::sys_common::util::report_overflow;
99
100         let guard = thread_info::stack_guard().unwrap_or(0..0);
101         let addr = siginfo_si_addr(info);
102
103         // If the faulting address is within the guard page, then we print a
104         // message saying so and abort.
105         if guard.start <= addr && addr < guard.end {
106             report_overflow();
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.
115         }
116     }
117
118     static MAIN_ALTSTACK: AtomicPtr<libc::c_void> = AtomicPtr::new(ptr::null_mut());
119     static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false);
120
121     pub unsafe fn init() {
122         let mut action: sigaction = mem::zeroed();
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());
130                 NEED_ALTSTACK.store(true, Ordering::Relaxed);
131             }
132         }
133
134         let handler = make_handler();
135         MAIN_ALTSTACK.store(handler._data, Ordering::Relaxed);
136         mem::forget(handler);
137     }
138
139     pub unsafe fn cleanup() {
140         Handler { _data: MAIN_ALTSTACK.load(Ordering::Relaxed) };
141     }
142
143     unsafe fn get_stackp() -> *mut libc::c_void {
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         );
152         if stackp == MAP_FAILED {
153             panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
154         }
155         let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE);
156         if guard_result != 0 {
157             panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error());
158         }
159         stackp.add(page_size())
160     }
161
162     #[cfg(any(
163         target_os = "linux",
164         target_os = "macos",
165         target_os = "freebsd",
166         target_os = "netbsd",
167         target_os = "openbsd",
168         target_os = "solaris",
169         target_os = "illumos"
170     ))]
171     unsafe fn get_stack() -> libc::stack_t {
172         libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
173     }
174
175     #[cfg(target_os = "dragonfly")]
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     }
179
180     pub unsafe fn make_handler() -> Handler {
181         if !NEED_ALTSTACK.load(Ordering::Relaxed) {
182             return Handler::null();
183         }
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 {
192             Handler::null()
193         }
194     }
195
196     pub unsafe fn drop_handler(handler: &mut Handler) {
197         if !handler._data.is_null() {
198             let stack = libc::stack_t {
199                 ss_sp: ptr::null_mut(),
200                 ss_flags: SS_DISABLE,
201                 // Workaround for bug in macOS implementation of sigaltstack
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());
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());
211         }
212     }
213 }
214
215 #[cfg(not(any(
216     target_os = "linux",
217     target_os = "macos",
218     target_os = "dragonfly",
219     target_os = "freebsd",
220     target_os = "solaris",
221     target_os = "illumos",
222     target_os = "netbsd",
223     target_os = "openbsd",
224 )))]
225 mod imp {
226     pub unsafe fn init() {}
227
228     pub unsafe fn cleanup() {}
229
230     pub unsafe fn make_handler() -> super::Handler {
231         super::Handler::null()
232     }
233
234     pub unsafe fn drop_handler(_handler: &mut super::Handler) {}
235 }