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