1#![cfg_attr(test, allow(dead_code))]
2
3pub use self::imp::{cleanup, init};
4use self::imp::{drop_handler, make_handler};
5
6pub struct Handler {
7 data: *mut libc::c_void,
8}
9
10impl Handler {
11 pub unsafe fn new(thread_name: Option<Box<str>>) -> Handler {
12 make_handler(false, thread_name)
13 }
14
15 fn null() -> Handler {
16 Handler { data: crate::ptr::null_mut() }
17 }
18}
19
20impl Drop for Handler {
21 fn drop(&mut self) {
22 unsafe {
23 drop_handler(self.data);
24 }
25 }
26}
27
28#[cfg(all(
29 not(miri),
30 any(
31 target_os = "linux",
32 target_os = "freebsd",
33 target_os = "hurd",
34 target_os = "macos",
35 target_os = "netbsd",
36 target_os = "openbsd",
37 target_os = "solaris",
38 target_os = "illumos",
39 ),
40))]
41mod thread_info;
42
43#[cfg(all(
47 not(miri),
48 any(
49 target_os = "linux",
50 target_os = "freebsd",
51 target_os = "hurd",
52 target_os = "macos",
53 target_os = "netbsd",
54 target_os = "openbsd",
55 target_os = "solaris",
56 target_os = "illumos",
57 )
58))]
59mod imp {
60 use libc::{
61 MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK,
62 SA_SIGINFO, SIG_DFL, SIGBUS, SIGSEGV, SS_DISABLE, sigaction, sigaltstack, sighandler_t,
63 };
64 #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
65 use libc::{mmap as mmap64, mprotect, munmap};
66 #[cfg(all(target_os = "linux", target_env = "gnu"))]
67 use libc::{mmap64, mprotect, munmap};
68
69 use super::Handler;
70 use super::thread_info::{delete_current_info, set_current_info, with_current_info};
71 use crate::ops::Range;
72 use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
73 use crate::sys::pal::unix::os;
74 use crate::{io, mem, ptr};
75
76 #[forbid(unsafe_op_in_unsafe_fn)]
101 unsafe extern "C" fn signal_handler(
102 signum: libc::c_int,
103 info: *mut libc::siginfo_t,
104 _data: *mut libc::c_void,
105 ) {
106 let fault_addr = unsafe { (*info).si_addr().addr() };
108
109 if fault_addr != 0 {
114 with_current_info(|thread_info| {
115 if let Some(thread_info) = thread_info
118 && thread_info.guard_page_range.contains(&fault_addr)
119 {
120 let name = thread_info.thread_name.as_deref().unwrap_or("<unknown>");
121 let tid = crate::thread::current_os_id();
122 rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n");
123 rtabort!("stack overflow");
124 }
125 })
126 }
127
128 let mut action: sigaction = unsafe { mem::zeroed() };
131 action.sa_sigaction = SIG_DFL;
132 unsafe { sigaction(signum, &action, ptr::null_mut()) };
134
135 }
137
138 static PAGE_SIZE: Atomic<usize> = AtomicUsize::new(0);
139 static MAIN_ALTSTACK: Atomic<*mut libc::c_void> = AtomicPtr::new(ptr::null_mut());
140 static NEED_ALTSTACK: Atomic<bool> = AtomicBool::new(false);
141
142 #[forbid(unsafe_op_in_unsafe_fn)]
145 pub unsafe fn init() {
146 PAGE_SIZE.store(os::page_size(), Ordering::Relaxed);
147
148 let mut guard_page_range = unsafe { install_main_guard() };
149
150 if cfg!(panic = "immediate-abort") {
154 return;
155 }
156
157 let mut action: sigaction = unsafe { mem::zeroed() };
159 for &signal in &[SIGSEGV, SIGBUS] {
160 unsafe { sigaction(signal, ptr::null_mut(), &mut action) };
162 if action.sa_sigaction == SIG_DFL {
164 if !NEED_ALTSTACK.load(Ordering::Relaxed) {
165 NEED_ALTSTACK.store(true, Ordering::Release);
167 let handler = unsafe { make_handler(true, None) };
168 MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
169 mem::forget(handler);
170
171 if let Some(guard_page_range) = guard_page_range.take() {
172 set_current_info(guard_page_range, Some(Box::from("main")));
173 }
174 }
175
176 action.sa_flags = SA_SIGINFO | SA_ONSTACK;
177 action.sa_sigaction = signal_handler
178 as unsafe extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)
179 as sighandler_t;
180 unsafe { sigaction(signal, &action, ptr::null_mut()) };
182 }
183 }
184 }
185
186 #[forbid(unsafe_op_in_unsafe_fn)]
189 pub unsafe fn cleanup() {
190 if cfg!(panic = "immediate-abort") {
191 return;
192 }
193 unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) };
196 }
197
198 unsafe fn get_stack() -> libc::stack_t {
199 #[cfg(any(
203 target_os = "openbsd",
204 target_os = "netbsd",
205 target_os = "linux",
206 target_os = "dragonfly",
207 ))]
208 let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
209 #[cfg(not(any(
210 target_os = "openbsd",
211 target_os = "netbsd",
212 target_os = "linux",
213 target_os = "dragonfly",
214 )))]
215 let flags = MAP_PRIVATE | MAP_ANON;
216
217 let sigstack_size = sigstack_size();
218 let page_size = PAGE_SIZE.load(Ordering::Relaxed);
219
220 let stackp = mmap64(
221 ptr::null_mut(),
222 sigstack_size + page_size,
223 PROT_READ | PROT_WRITE,
224 flags,
225 -1,
226 0,
227 );
228 if stackp == MAP_FAILED {
229 panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
230 }
231 let guard_result = libc::mprotect(stackp, page_size, PROT_NONE);
232 if guard_result != 0 {
233 panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error());
234 }
235 let stackp = stackp.add(page_size);
236
237 libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size }
238 }
239
240 #[forbid(unsafe_op_in_unsafe_fn)]
243 pub unsafe fn make_handler(main_thread: bool, thread_name: Option<Box<str>>) -> Handler {
244 if cfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) {
245 return Handler::null();
246 }
247
248 if !main_thread {
249 if let Some(guard_page_range) = unsafe { current_guard() } {
250 set_current_info(guard_page_range, thread_name);
251 }
252 }
253
254 let mut stack = unsafe { mem::zeroed() };
256 unsafe { sigaltstack(ptr::null(), &mut stack) };
258 if stack.ss_flags & SS_DISABLE != 0 {
260 unsafe {
262 stack = get_stack();
263 sigaltstack(&stack, ptr::null_mut());
264 }
265 Handler { data: stack.ss_sp as *mut libc::c_void }
266 } else {
267 Handler::null()
268 }
269 }
270
271 #[forbid(unsafe_op_in_unsafe_fn)]
277 pub unsafe fn drop_handler(data: *mut libc::c_void) {
278 if !data.is_null() {
279 let sigstack_size = sigstack_size();
280 let page_size = PAGE_SIZE.load(Ordering::Relaxed);
281 let disabling_stack = libc::stack_t {
282 ss_sp: ptr::null_mut(),
283 ss_flags: SS_DISABLE,
284 ss_size: sigstack_size,
289 };
290 unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) };
292 unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };
295 }
296
297 delete_current_info();
298 }
299
300 #[cfg(any(target_os = "linux", target_os = "android"))]
302 fn sigstack_size() -> usize {
303 let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) };
304 libc::SIGSTKSZ.max(dynamic_sigstksz as _)
308 }
309
310 #[cfg(not(any(target_os = "linux", target_os = "android")))]
312 fn sigstack_size() -> usize {
313 libc::SIGSTKSZ
314 }
315
316 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
317 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
318 let mut current_stack: libc::stack_t = crate::mem::zeroed();
319 assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
320 Some(current_stack.ss_sp)
321 }
322
323 #[cfg(target_os = "macos")]
324 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
325 let th = libc::pthread_self();
326 let stackptr = libc::pthread_get_stackaddr_np(th);
327 Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th)))
328 }
329
330 #[cfg(target_os = "openbsd")]
331 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
332 let mut current_stack: libc::stack_t = crate::mem::zeroed();
333 assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
334
335 let stack_ptr = current_stack.ss_sp;
336 let stackaddr = if libc::pthread_main_np() == 1 {
337 stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
339 } else {
340 stack_ptr.addr() - current_stack.ss_size
342 };
343 Some(stack_ptr.with_addr(stackaddr))
344 }
345
346 #[cfg(any(
347 target_os = "android",
348 target_os = "freebsd",
349 target_os = "netbsd",
350 target_os = "hurd",
351 target_os = "linux",
352 target_os = "l4re"
353 ))]
354 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
355 let mut ret = None;
356 let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
357 if !cfg!(target_os = "freebsd") {
358 attr = mem::MaybeUninit::zeroed();
359 }
360 #[cfg(target_os = "freebsd")]
361 assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
362 #[cfg(target_os = "freebsd")]
363 let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr());
364 #[cfg(not(target_os = "freebsd"))]
365 let e = libc::pthread_getattr_np(libc::pthread_self(), attr.as_mut_ptr());
366 if e == 0 {
367 let mut stackaddr = crate::ptr::null_mut();
368 let mut stacksize = 0;
369 assert_eq!(
370 libc::pthread_attr_getstack(attr.as_ptr(), &mut stackaddr, &mut stacksize),
371 0
372 );
373 ret = Some(stackaddr);
374 }
375 if e == 0 || cfg!(target_os = "freebsd") {
376 assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0);
377 }
378 ret
379 }
380
381 fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> {
382 let stackptr = unsafe { get_stack_start()? };
383 let stackaddr = stackptr.addr();
384
385 let remainder = stackaddr % page_size;
392 Some(if remainder == 0 {
393 stackptr
394 } else {
395 stackptr.with_addr(stackaddr + page_size - remainder)
396 })
397 }
398
399 #[forbid(unsafe_op_in_unsafe_fn)]
400 unsafe fn install_main_guard() -> Option<Range<usize>> {
401 let page_size = PAGE_SIZE.load(Ordering::Relaxed);
402
403 unsafe {
404 if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
406 install_main_guard_linux(page_size)
407 } else if cfg!(all(target_os = "linux", target_env = "musl")) {
408 install_main_guard_linux_musl(page_size)
409 } else if cfg!(target_os = "freebsd") {
410 #[cfg(not(target_os = "freebsd"))]
411 return None;
412 #[cfg(target_os = "freebsd")]
414 install_main_guard_freebsd(page_size)
415 } else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) {
416 install_main_guard_bsds(page_size)
417 } else {
418 install_main_guard_default(page_size)
419 }
420 }
421 }
422
423 #[forbid(unsafe_op_in_unsafe_fn)]
424 unsafe fn install_main_guard_linux(page_size: usize) -> Option<Range<usize>> {
425 let stackptr = stack_start_aligned(page_size)?;
436 let stackaddr = stackptr.addr();
437 Some(stackaddr - page_size..stackaddr)
438 }
439
440 #[forbid(unsafe_op_in_unsafe_fn)]
441 unsafe fn install_main_guard_linux_musl(_page_size: usize) -> Option<Range<usize>> {
442 None
447 }
448
449 #[forbid(unsafe_op_in_unsafe_fn)]
450 #[cfg(target_os = "freebsd")]
451 unsafe fn install_main_guard_freebsd(page_size: usize) -> Option<Range<usize>> {
452 let stackptr = stack_start_aligned(page_size)?;
457 let guardaddr = stackptr.addr();
458 static PAGES: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new();
463
464 let pages = PAGES.get_or_init(|| {
465 let mut guard: usize = 0;
466 let mut size = size_of_val(&guard);
467 let oid = c"security.bsd.stack_guard_page";
468
469 let r = unsafe {
470 libc::sysctlbyname(
471 oid.as_ptr(),
472 (&raw mut guard).cast(),
473 &raw mut size,
474 ptr::null_mut(),
475 0,
476 )
477 };
478 if r == 0 { guard } else { 1 }
479 });
480 Some(guardaddr..guardaddr + pages * page_size)
481 }
482
483 #[forbid(unsafe_op_in_unsafe_fn)]
484 unsafe fn install_main_guard_bsds(page_size: usize) -> Option<Range<usize>> {
485 let stackptr = stack_start_aligned(page_size)?;
493 let stackaddr = stackptr.addr();
494 Some(stackaddr - page_size..stackaddr)
495 }
496
497 #[forbid(unsafe_op_in_unsafe_fn)]
498 unsafe fn install_main_guard_default(page_size: usize) -> Option<Range<usize>> {
499 let stackptr = stack_start_aligned(page_size)?;
508 let result = unsafe {
509 mmap64(
510 stackptr,
511 page_size,
512 PROT_READ | PROT_WRITE,
513 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
514 -1,
515 0,
516 )
517 };
518 if result != stackptr || result == MAP_FAILED {
519 panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
520 }
521
522 let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) };
523 if result != 0 {
524 panic!("failed to protect the guard page: {}", io::Error::last_os_error());
525 }
526
527 let guardaddr = stackptr.addr();
528
529 Some(guardaddr..guardaddr + page_size)
530 }
531
532 #[cfg(any(
533 target_os = "macos",
534 target_os = "openbsd",
535 target_os = "solaris",
536 target_os = "illumos",
537 ))]
538 unsafe fn current_guard() -> Option<Range<usize>> {
540 let stackptr = get_stack_start()?;
541 let stackaddr = stackptr.addr();
542 Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
543 }
544
545 #[cfg(any(
546 target_os = "android",
547 target_os = "freebsd",
548 target_os = "hurd",
549 target_os = "linux",
550 target_os = "netbsd",
551 target_os = "l4re"
552 ))]
553 unsafe fn current_guard() -> Option<Range<usize>> {
555 let mut ret = None;
556
557 let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
558 if !cfg!(target_os = "freebsd") {
559 attr = mem::MaybeUninit::zeroed();
560 }
561 #[cfg(target_os = "freebsd")]
562 assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
563 #[cfg(target_os = "freebsd")]
564 let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr());
565 #[cfg(not(target_os = "freebsd"))]
566 let e = libc::pthread_getattr_np(libc::pthread_self(), attr.as_mut_ptr());
567 if e == 0 {
568 let mut guardsize = 0;
569 assert_eq!(libc::pthread_attr_getguardsize(attr.as_ptr(), &mut guardsize), 0);
570 if guardsize == 0 {
571 if cfg!(all(target_os = "linux", target_env = "musl")) {
572 guardsize = PAGE_SIZE.load(Ordering::Relaxed);
576 } else {
577 panic!("there is no guard page");
578 }
579 }
580 let mut stackptr = crate::ptr::null_mut::<libc::c_void>();
581 let mut size = 0;
582 assert_eq!(libc::pthread_attr_getstack(attr.as_ptr(), &mut stackptr, &mut size), 0);
583
584 let stackaddr = stackptr.addr();
585 ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) {
586 Some(stackaddr - guardsize..stackaddr)
587 } else if cfg!(all(target_os = "linux", target_env = "musl")) {
588 Some(stackaddr - guardsize..stackaddr)
589 } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
590 {
591 Some(stackaddr - guardsize..stackaddr + guardsize)
598 } else {
599 Some(stackaddr..stackaddr + guardsize)
600 };
601 }
602 if e == 0 || cfg!(target_os = "freebsd") {
603 assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0);
604 }
605 ret
606 }
607}
608
609#[cfg(any(
618 miri,
619 not(any(
620 target_os = "linux",
621 target_os = "freebsd",
622 target_os = "hurd",
623 target_os = "macos",
624 target_os = "netbsd",
625 target_os = "openbsd",
626 target_os = "solaris",
627 target_os = "illumos",
628 target_os = "cygwin",
629 ))
630))]
631mod imp {
632 pub unsafe fn init() {}
633
634 pub unsafe fn cleanup() {}
635
636 pub unsafe fn make_handler(
637 _main_thread: bool,
638 _thread_name: Option<Box<str>>,
639 ) -> super::Handler {
640 super::Handler::null()
641 }
642
643 pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
644}
645
646#[cfg(target_os = "cygwin")]
647mod imp {
648 mod c {
649 pub type PVECTORED_EXCEPTION_HANDLER =
650 Option<unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32>;
651 pub type NTSTATUS = i32;
652 pub type BOOL = i32;
653
654 unsafe extern "system" {
655 pub fn AddVectoredExceptionHandler(
656 first: u32,
657 handler: PVECTORED_EXCEPTION_HANDLER,
658 ) -> *mut core::ffi::c_void;
659 pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL;
660 }
661
662 pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _;
663 pub const EXCEPTION_CONTINUE_SEARCH: i32 = 1i32;
664
665 #[repr(C)]
666 #[derive(Clone, Copy)]
667 pub struct EXCEPTION_POINTERS {
668 pub ExceptionRecord: *mut EXCEPTION_RECORD,
669 }
672 #[repr(C)]
673 #[derive(Clone, Copy)]
674 pub struct EXCEPTION_RECORD {
675 pub ExceptionCode: NTSTATUS,
676 pub ExceptionFlags: u32,
677 pub ExceptionRecord: *mut EXCEPTION_RECORD,
678 pub ExceptionAddress: *mut core::ffi::c_void,
679 pub NumberParameters: u32,
680 pub ExceptionInformation: [usize; 15],
681 }
682 }
683
684 fn reserve_stack() {
686 let result = unsafe { c::SetThreadStackGuarantee(&mut 0x5000) };
687 debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling");
690 }
691
692 unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> i32 {
693 unsafe {
695 let rec = &(*(*ExceptionInfo).ExceptionRecord);
696 let code = rec.ExceptionCode;
697
698 if code == c::EXCEPTION_STACK_OVERFLOW {
699 crate::thread::with_current_name(|name| {
700 let name = name.unwrap_or("<unknown>");
701 let tid = crate::thread::current_os_id();
702 rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n");
703 });
704 }
705 c::EXCEPTION_CONTINUE_SEARCH
706 }
707 }
708
709 pub unsafe fn init() {
710 unsafe {
712 let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler));
713 debug_assert!(!result.is_null(), "failed to install exception handler");
716 }
717 reserve_stack();
719 }
720
721 pub unsafe fn cleanup() {}
722
723 pub unsafe fn make_handler(
724 main_thread: bool,
725 _thread_name: Option<Box<str>>,
726 ) -> super::Handler {
727 if !main_thread {
728 reserve_stack();
729 }
730 super::Handler::null()
731 }
732
733 pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
734}