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::OnceLock;
73 use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
74 use crate::sys::pal::unix::os;
75 use crate::{io, mem, panic, ptr};
76
77 #[forbid(unsafe_op_in_unsafe_fn)]
102 unsafe extern "C" fn signal_handler(
103 signum: libc::c_int,
104 info: *mut libc::siginfo_t,
105 _data: *mut libc::c_void,
106 ) {
107 let fault_addr = unsafe { (*info).si_addr().addr() };
109
110 if fault_addr != 0 {
115 with_current_info(|thread_info| {
116 if let Some(thread_info) = thread_info
119 && thread_info.guard_page_range.contains(&fault_addr)
120 {
121 let name = thread_info.thread_name.as_deref().unwrap_or("<unknown>");
122 rtprintpanic!("\nthread '{name}' 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 let mut action: sigaction = unsafe { mem::zeroed() };
152 for &signal in &[SIGSEGV, SIGBUS] {
153 unsafe { sigaction(signal, ptr::null_mut(), &mut action) };
155 if action.sa_sigaction == SIG_DFL {
157 if !NEED_ALTSTACK.load(Ordering::Relaxed) {
158 NEED_ALTSTACK.store(true, Ordering::Release);
160 let handler = unsafe { make_handler(true, None) };
161 MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
162 mem::forget(handler);
163
164 if let Some(guard_page_range) = guard_page_range.take() {
165 set_current_info(guard_page_range, Some(Box::from("main")));
166 }
167 }
168
169 action.sa_flags = SA_SIGINFO | SA_ONSTACK;
170 action.sa_sigaction = signal_handler as sighandler_t;
171 unsafe { sigaction(signal, &action, ptr::null_mut()) };
173 }
174 }
175 }
176
177 #[forbid(unsafe_op_in_unsafe_fn)]
180 pub unsafe fn cleanup() {
181 unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) };
184 }
185
186 unsafe fn get_stack() -> libc::stack_t {
187 #[cfg(any(
191 target_os = "openbsd",
192 target_os = "netbsd",
193 target_os = "linux",
194 target_os = "dragonfly",
195 ))]
196 let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
197 #[cfg(not(any(
198 target_os = "openbsd",
199 target_os = "netbsd",
200 target_os = "linux",
201 target_os = "dragonfly",
202 )))]
203 let flags = MAP_PRIVATE | MAP_ANON;
204
205 let sigstack_size = sigstack_size();
206 let page_size = PAGE_SIZE.load(Ordering::Relaxed);
207
208 let stackp = mmap64(
209 ptr::null_mut(),
210 sigstack_size + page_size,
211 PROT_READ | PROT_WRITE,
212 flags,
213 -1,
214 0,
215 );
216 if stackp == MAP_FAILED {
217 panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
218 }
219 let guard_result = libc::mprotect(stackp, page_size, PROT_NONE);
220 if guard_result != 0 {
221 panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error());
222 }
223 let stackp = stackp.add(page_size);
224
225 libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size }
226 }
227
228 #[forbid(unsafe_op_in_unsafe_fn)]
231 pub unsafe fn make_handler(main_thread: bool, thread_name: Option<Box<str>>) -> Handler {
232 if !NEED_ALTSTACK.load(Ordering::Acquire) {
233 return Handler::null();
234 }
235
236 if !main_thread {
237 if let Some(guard_page_range) = unsafe { current_guard() } {
238 set_current_info(guard_page_range, thread_name);
239 }
240 }
241
242 let mut stack = unsafe { mem::zeroed() };
244 unsafe { sigaltstack(ptr::null(), &mut stack) };
246 if stack.ss_flags & SS_DISABLE != 0 {
248 unsafe {
250 stack = get_stack();
251 sigaltstack(&stack, ptr::null_mut());
252 }
253 Handler { data: stack.ss_sp as *mut libc::c_void }
254 } else {
255 Handler::null()
256 }
257 }
258
259 #[forbid(unsafe_op_in_unsafe_fn)]
265 pub unsafe fn drop_handler(data: *mut libc::c_void) {
266 if !data.is_null() {
267 let sigstack_size = sigstack_size();
268 let page_size = PAGE_SIZE.load(Ordering::Relaxed);
269 let disabling_stack = libc::stack_t {
270 ss_sp: ptr::null_mut(),
271 ss_flags: SS_DISABLE,
272 ss_size: sigstack_size,
277 };
278 unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) };
280 unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };
283 }
284
285 delete_current_info();
286 }
287
288 #[cfg(any(target_os = "linux", target_os = "android"))]
290 fn sigstack_size() -> usize {
291 let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) };
292 libc::SIGSTKSZ.max(dynamic_sigstksz as _)
296 }
297
298 #[cfg(not(any(target_os = "linux", target_os = "android")))]
300 fn sigstack_size() -> usize {
301 libc::SIGSTKSZ
302 }
303
304 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
305 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
306 let mut current_stack: libc::stack_t = crate::mem::zeroed();
307 assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
308 Some(current_stack.ss_sp)
309 }
310
311 #[cfg(target_os = "macos")]
312 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
313 let th = libc::pthread_self();
314 let stackptr = libc::pthread_get_stackaddr_np(th);
315 Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th)))
316 }
317
318 #[cfg(target_os = "openbsd")]
319 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
320 let mut current_stack: libc::stack_t = crate::mem::zeroed();
321 assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
322
323 let stack_ptr = current_stack.ss_sp;
324 let stackaddr = if libc::pthread_main_np() == 1 {
325 stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
327 } else {
328 stack_ptr.addr() - current_stack.ss_size
330 };
331 Some(stack_ptr.with_addr(stackaddr))
332 }
333
334 #[cfg(any(
335 target_os = "android",
336 target_os = "freebsd",
337 target_os = "netbsd",
338 target_os = "hurd",
339 target_os = "linux",
340 target_os = "l4re"
341 ))]
342 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
343 let mut ret = None;
344 let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
345 if !cfg!(target_os = "freebsd") {
346 attr = mem::MaybeUninit::zeroed();
347 }
348 #[cfg(target_os = "freebsd")]
349 assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
350 #[cfg(target_os = "freebsd")]
351 let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr());
352 #[cfg(not(target_os = "freebsd"))]
353 let e = libc::pthread_getattr_np(libc::pthread_self(), attr.as_mut_ptr());
354 if e == 0 {
355 let mut stackaddr = crate::ptr::null_mut();
356 let mut stacksize = 0;
357 assert_eq!(
358 libc::pthread_attr_getstack(attr.as_ptr(), &mut stackaddr, &mut stacksize),
359 0
360 );
361 ret = Some(stackaddr);
362 }
363 if e == 0 || cfg!(target_os = "freebsd") {
364 assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0);
365 }
366 ret
367 }
368
369 fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> {
370 let stackptr = unsafe { get_stack_start()? };
371 let stackaddr = stackptr.addr();
372
373 let remainder = stackaddr % page_size;
380 Some(if remainder == 0 {
381 stackptr
382 } else {
383 stackptr.with_addr(stackaddr + page_size - remainder)
384 })
385 }
386
387 #[forbid(unsafe_op_in_unsafe_fn)]
388 unsafe fn install_main_guard() -> Option<Range<usize>> {
389 let page_size = PAGE_SIZE.load(Ordering::Relaxed);
390
391 unsafe {
392 if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
394 install_main_guard_linux(page_size)
395 } else if cfg!(all(target_os = "linux", target_env = "musl")) {
396 install_main_guard_linux_musl(page_size)
397 } else if cfg!(target_os = "freebsd") {
398 install_main_guard_freebsd(page_size)
399 } else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) {
400 install_main_guard_bsds(page_size)
401 } else {
402 install_main_guard_default(page_size)
403 }
404 }
405 }
406
407 #[forbid(unsafe_op_in_unsafe_fn)]
408 unsafe fn install_main_guard_linux(page_size: usize) -> Option<Range<usize>> {
409 let stackptr = stack_start_aligned(page_size)?;
420 let stackaddr = stackptr.addr();
421 Some(stackaddr - page_size..stackaddr)
422 }
423
424 #[forbid(unsafe_op_in_unsafe_fn)]
425 unsafe fn install_main_guard_linux_musl(_page_size: usize) -> Option<Range<usize>> {
426 None
431 }
432
433 #[forbid(unsafe_op_in_unsafe_fn)]
434 unsafe fn install_main_guard_freebsd(page_size: usize) -> Option<Range<usize>> {
435 let stackptr = stack_start_aligned(page_size)?;
440 let guardaddr = stackptr.addr();
441 static PAGES: OnceLock<usize> = OnceLock::new();
446
447 let pages = PAGES.get_or_init(|| {
448 use crate::sys::weak::dlsym;
449 dlsym!(
450 fn sysctlbyname(
451 name: *const libc::c_char,
452 oldp: *mut libc::c_void,
453 oldlenp: *mut libc::size_t,
454 newp: *const libc::c_void,
455 newlen: libc::size_t,
456 ) -> libc::c_int;
457 );
458 let mut guard: usize = 0;
459 let mut size = size_of_val(&guard);
460 let oid = c"security.bsd.stack_guard_page";
461 match sysctlbyname.get() {
462 Some(fcn)
463 if unsafe {
464 fcn(
465 oid.as_ptr(),
466 (&raw mut guard).cast(),
467 &raw mut size,
468 ptr::null_mut(),
469 0,
470 ) == 0
471 } =>
472 {
473 guard
474 }
475 _ => 1,
476 }
477 });
478 Some(guardaddr..guardaddr + pages * page_size)
479 }
480
481 #[forbid(unsafe_op_in_unsafe_fn)]
482 unsafe fn install_main_guard_bsds(page_size: usize) -> Option<Range<usize>> {
483 let stackptr = stack_start_aligned(page_size)?;
491 let stackaddr = stackptr.addr();
492 Some(stackaddr - page_size..stackaddr)
493 }
494
495 #[forbid(unsafe_op_in_unsafe_fn)]
496 unsafe fn install_main_guard_default(page_size: usize) -> Option<Range<usize>> {
497 let stackptr = stack_start_aligned(page_size)?;
506 let result = unsafe {
507 mmap64(
508 stackptr,
509 page_size,
510 PROT_READ | PROT_WRITE,
511 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
512 -1,
513 0,
514 )
515 };
516 if result != stackptr || result == MAP_FAILED {
517 panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
518 }
519
520 let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) };
521 if result != 0 {
522 panic!("failed to protect the guard page: {}", io::Error::last_os_error());
523 }
524
525 let guardaddr = stackptr.addr();
526
527 Some(guardaddr..guardaddr + page_size)
528 }
529
530 #[cfg(any(
531 target_os = "macos",
532 target_os = "openbsd",
533 target_os = "solaris",
534 target_os = "illumos",
535 ))]
536 unsafe fn current_guard() -> Option<Range<usize>> {
538 let stackptr = get_stack_start()?;
539 let stackaddr = stackptr.addr();
540 Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
541 }
542
543 #[cfg(any(
544 target_os = "android",
545 target_os = "freebsd",
546 target_os = "hurd",
547 target_os = "linux",
548 target_os = "netbsd",
549 target_os = "l4re"
550 ))]
551 unsafe fn current_guard() -> Option<Range<usize>> {
553 let mut ret = None;
554
555 let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
556 if !cfg!(target_os = "freebsd") {
557 attr = mem::MaybeUninit::zeroed();
558 }
559 #[cfg(target_os = "freebsd")]
560 assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
561 #[cfg(target_os = "freebsd")]
562 let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr());
563 #[cfg(not(target_os = "freebsd"))]
564 let e = libc::pthread_getattr_np(libc::pthread_self(), attr.as_mut_ptr());
565 if e == 0 {
566 let mut guardsize = 0;
567 assert_eq!(libc::pthread_attr_getguardsize(attr.as_ptr(), &mut guardsize), 0);
568 if guardsize == 0 {
569 if cfg!(all(target_os = "linux", target_env = "musl")) {
570 guardsize = PAGE_SIZE.load(Ordering::Relaxed);
574 } else {
575 panic!("there is no guard page");
576 }
577 }
578 let mut stackptr = crate::ptr::null_mut::<libc::c_void>();
579 let mut size = 0;
580 assert_eq!(libc::pthread_attr_getstack(attr.as_ptr(), &mut stackptr, &mut size), 0);
581
582 let stackaddr = stackptr.addr();
583 ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) {
584 Some(stackaddr - guardsize..stackaddr)
585 } else if cfg!(all(target_os = "linux", target_env = "musl")) {
586 Some(stackaddr - guardsize..stackaddr)
587 } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
588 {
589 Some(stackaddr - guardsize..stackaddr + guardsize)
596 } else {
597 Some(stackaddr..stackaddr + guardsize)
598 };
599 }
600 if e == 0 || cfg!(target_os = "freebsd") {
601 assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0);
602 }
603 ret
604 }
605}
606
607#[cfg(any(
616 miri,
617 not(any(
618 target_os = "linux",
619 target_os = "freebsd",
620 target_os = "hurd",
621 target_os = "macos",
622 target_os = "netbsd",
623 target_os = "openbsd",
624 target_os = "solaris",
625 target_os = "illumos",
626 target_os = "cygwin",
627 ))
628))]
629mod imp {
630 pub unsafe fn init() {}
631
632 pub unsafe fn cleanup() {}
633
634 pub unsafe fn make_handler(
635 _main_thread: bool,
636 _thread_name: Option<Box<str>>,
637 ) -> super::Handler {
638 super::Handler::null()
639 }
640
641 pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
642}
643
644#[cfg(target_os = "cygwin")]
645mod imp {
646 mod c {
647 pub type PVECTORED_EXCEPTION_HANDLER =
648 Option<unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32>;
649 pub type NTSTATUS = i32;
650 pub type BOOL = i32;
651
652 unsafe extern "system" {
653 pub fn AddVectoredExceptionHandler(
654 first: u32,
655 handler: PVECTORED_EXCEPTION_HANDLER,
656 ) -> *mut core::ffi::c_void;
657 pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL;
658 }
659
660 pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _;
661 pub const EXCEPTION_CONTINUE_SEARCH: i32 = 1i32;
662
663 #[repr(C)]
664 #[derive(Clone, Copy)]
665 pub struct EXCEPTION_POINTERS {
666 pub ExceptionRecord: *mut EXCEPTION_RECORD,
667 }
670 #[repr(C)]
671 #[derive(Clone, Copy)]
672 pub struct EXCEPTION_RECORD {
673 pub ExceptionCode: NTSTATUS,
674 pub ExceptionFlags: u32,
675 pub ExceptionRecord: *mut EXCEPTION_RECORD,
676 pub ExceptionAddress: *mut core::ffi::c_void,
677 pub NumberParameters: u32,
678 pub ExceptionInformation: [usize; 15],
679 }
680 }
681
682 fn reserve_stack() {
684 let result = unsafe { c::SetThreadStackGuarantee(&mut 0x5000) };
685 debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling");
688 }
689
690 unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> i32 {
691 unsafe {
693 let rec = &(*(*ExceptionInfo).ExceptionRecord);
694 let code = rec.ExceptionCode;
695
696 if code == c::EXCEPTION_STACK_OVERFLOW {
697 crate::thread::with_current_name(|name| {
698 let name = name.unwrap_or("<unknown>");
699 rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
700 });
701 }
702 c::EXCEPTION_CONTINUE_SEARCH
703 }
704 }
705
706 pub unsafe fn init() {
707 unsafe {
709 let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler));
710 debug_assert!(!result.is_null(), "failed to install exception handler");
713 }
714 reserve_stack();
716 }
717
718 pub unsafe fn cleanup() {}
719
720 pub unsafe fn make_handler(
721 main_thread: bool,
722 _thread_name: Option<Box<str>>,
723 ) -> super::Handler {
724 if !main_thread {
725 reserve_stack();
726 }
727 super::Handler::null()
728 }
729
730 pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
731}