Skip to main content

std/sys/pal/unix/
stack_overflow.rs

1#![cfg_attr(test, allow(dead_code))]
2
3pub use self::imp::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() -> Handler {
12        make_handler(false)
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// miri doesn't model signals nor stack overflows and this code has some
44// synchronization properties that we don't want to expose to user code,
45// hence we disable it on miri.
46#[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::conf;
74    use crate::{io, mem, ptr};
75
76    // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
77    // (unmapped pages) at the end of every thread's stack, so if a thread ends
78    // up running into the guard page it'll trigger this handler. We want to
79    // detect these cases and print out a helpful error saying that the stack
80    // has overflowed. All other signals, however, should go back to what they
81    // were originally supposed to do.
82    //
83    // This handler currently exists purely to print an informative message
84    // whenever a thread overflows its stack. We then abort to exit and
85    // indicate a crash, but to avoid a misleading SIGSEGV that might lead
86    // users to believe that unsafe code has accessed an invalid pointer; the
87    // SIGSEGV encountered when overflowing the stack is expected and
88    // well-defined.
89    //
90    // If this is not a stack overflow, the handler un-registers itself and
91    // then returns (to allow the original signal to be delivered again).
92    // Returning from this kind of signal handler is technically not defined
93    // to work when reading the POSIX spec strictly, but in practice it turns
94    // out many large systems and all implementations allow returning from a
95    // signal handler to work. For a more detailed explanation see the
96    // comments on #26458.
97    /// SIGSEGV/SIGBUS entry point
98    /// # Safety
99    /// Rust doesn't call this, it *gets called*.
100    #[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        // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`.
107        let fault_addr = unsafe { (*info).si_addr().addr() };
108
109        // `with_current_info` expects that the process aborts after it is
110        // called. If the signal was not caused by a memory access, this might
111        // not be true. We detect this by noticing that the `si_addr` field is
112        // zero if the signal is synthetic.
113        if fault_addr != 0 {
114            with_current_info(|thread_info| {
115                // If the faulting address is within the guard page, then we print a
116                // message saying so and abort.
117                if let Some(thread_info) = thread_info
118                    && thread_info.guard_page_range.contains(&fault_addr)
119                {
120                    // Hey you! Yes, you modifying the stack overflow message!
121                    // Please make sure that all functions called here are
122                    // actually async-signal-safe. If they're not, try retrieving
123                    // the information beforehand and storing it in `ThreadInfo`.
124                    // Thank you!
125                    // - says Jonas after having had to watch his carefully
126                    //   written code get made unsound again.
127                    let tid = thread_info.tid;
128                    let name = thread_info.name.as_deref().unwrap_or("<unknown>");
129                    if let Some(mut out) = crate::sys::stdio::panic_output() {
    let _ =
        crate::io::Write::write_fmt(&mut out,
            format_args!("\nthread \'{0}\' ({1}) has overflowed its stack\n",
                name, tid));
};rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n");
130                    {
    if let Some(mut out) = crate::sys::stdio::panic_output() {
        let _ =
            crate::io::Write::write_fmt(&mut out,
                format_args!("fatal runtime error: {0}, aborting\n",
                    format_args!("stack overflow")));
    };
    crate::process::abort();
};rtabort!("stack overflow");
131                }
132            })
133        }
134
135        // Unregister ourselves by reverting back to the default behavior.
136        // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
137        let mut action: sigaction = unsafe { mem::zeroed() };
138        action.sa_sigaction = SIG_DFL;
139        // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
140        unsafe { sigaction(signum, &action, ptr::null_mut()) };
141
142        // See comment above for why this function returns.
143    }
144
145    static PAGE_SIZE: Atomic<usize> = AtomicUsize::new(0);
146    // Store a pointer to the allocation for the main thread's altstack so that
147    // tools like valgrind don't complain about a leaked unreachable allocation.
148    //
149    // If the main thread exits, the process will terminate so there's no use in
150    // freeing resources. It also means that the altstack is still installed
151    // while TLS destructors are run on the main thread (c.f. #111272).
152    static MAIN_ALTSTACK: Atomic<*mut libc::c_void> = AtomicPtr::new(ptr::null_mut());
153    static NEED_ALTSTACK: Atomic<bool> = AtomicBool::new(false);
154
155    /// # Safety
156    /// Must be called only once
157    #[forbid(unsafe_op_in_unsafe_fn)]
158    pub unsafe fn init() {
159        PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed);
160
161        let mut guard_page_range = unsafe { install_main_guard() };
162
163        // Even for panic=immediate-abort, installing the guard pages is important for soundness.
164        // That said, we do not care about giving nice stackoverflow messages via our custom
165        // signal handler, just exit early and let the user enjoy the segfault.
166        if falsecfg!(panic = "immediate-abort") {
167            return;
168        }
169
170        // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
171        let mut action: sigaction = unsafe { mem::zeroed() };
172        for &signal in &[SIGSEGV, SIGBUS] {
173            // SAFETY: just fetches the current signal handler into action
174            unsafe { sigaction(signal, ptr::null_mut(), &mut action) };
175            // Configure our signal handler if one is not already set.
176            if action.sa_sigaction == SIG_DFL {
177                if !NEED_ALTSTACK.load(Ordering::Relaxed) {
178                    // haven't set up our sigaltstack yet
179                    NEED_ALTSTACK.store(true, Ordering::Release);
180                    let handler = unsafe { make_handler(true) };
181                    MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
182                    mem::forget(handler);
183
184                    if let Some(guard_page_range) = guard_page_range.take() {
185                        set_current_info(guard_page_range);
186                    }
187                }
188
189                action.sa_flags = SA_SIGINFO | SA_ONSTACK;
190                action.sa_sigaction = signal_handler
191                    as unsafe extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)
192                    as sighandler_t;
193                // SAFETY: only overriding signals if the default is set
194                unsafe { sigaction(signal, &action, ptr::null_mut()) };
195            }
196        }
197    }
198
199    unsafe fn get_stack() -> libc::stack_t {
200        // OpenBSD requires this flag for stack mapping
201        // otherwise the said mapping will fail as a no-op on most systems
202        // and has a different meaning on FreeBSD
203        #[cfg(any(
204            target_os = "openbsd",
205            target_os = "netbsd",
206            target_os = "linux",
207            target_os = "dragonfly",
208        ))]
209        let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
210        #[cfg(not(any(
211            target_os = "openbsd",
212            target_os = "netbsd",
213            target_os = "linux",
214            target_os = "dragonfly",
215        )))]
216        let flags = MAP_PRIVATE | MAP_ANON;
217
218        let sigstack_size = sigstack_size();
219        let page_size = PAGE_SIZE.load(Ordering::Relaxed);
220
221        let stackp = mmap64(
222            ptr::null_mut(),
223            sigstack_size + page_size,
224            PROT_READ | PROT_WRITE,
225            flags,
226            -1,
227            0,
228        );
229        if stackp == MAP_FAILED {
230            {
    ::core::panicking::panic_fmt(format_args!("failed to allocate an alternative stack: {0}",
            io::Error::last_os_error()));
};panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
231        }
232        let guard_result = libc::mprotect(stackp, page_size, PROT_NONE);
233        if guard_result != 0 {
234            {
    ::core::panicking::panic_fmt(format_args!("failed to set up alternative stack guard page: {0}",
            io::Error::last_os_error()));
};panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error());
235        }
236        let stackp = stackp.add(page_size);
237
238        libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size }
239    }
240
241    /// # Safety
242    /// Mutates the alternate signal stack
243    #[forbid(unsafe_op_in_unsafe_fn)]
244    pub unsafe fn make_handler(main_thread: bool) -> Handler {
245        if falsecfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) {
246            return Handler::null();
247        }
248
249        if !main_thread {
250            if let Some(guard_page_range) = unsafe { current_guard() } {
251                set_current_info(guard_page_range);
252            }
253        }
254
255        // SAFETY: assuming stack_t is zero-initializable
256        let mut stack = unsafe { mem::zeroed() };
257        // SAFETY: reads current stack_t into stack
258        unsafe { sigaltstack(ptr::null(), &mut stack) };
259        // Configure alternate signal stack, if one is not already set.
260        if stack.ss_flags & SS_DISABLE != 0 {
261            // SAFETY: We warned our caller this would happen!
262            unsafe {
263                stack = get_stack();
264                sigaltstack(&stack, ptr::null_mut());
265            }
266            Handler { data: stack.ss_sp as *mut libc::c_void }
267        } else {
268            Handler::null()
269        }
270    }
271
272    /// # Safety
273    /// Must be called
274    /// - only with our handler or nullptr
275    /// - only when done with our altstack
276    /// This disables the alternate signal stack!
277    #[forbid(unsafe_op_in_unsafe_fn)]
278    pub unsafe fn drop_handler(data: *mut libc::c_void) {
279        if !data.is_null() {
280            let sigstack_size = sigstack_size();
281            let page_size = PAGE_SIZE.load(Ordering::Relaxed);
282            let disabling_stack = libc::stack_t {
283                ss_sp: ptr::null_mut(),
284                ss_flags: SS_DISABLE,
285                // Workaround for bug in macOS implementation of sigaltstack
286                // UNIX2003 which returns ENOMEM when disabling a stack while
287                // passing ss_size smaller than MINSIGSTKSZ. According to POSIX
288                // both ss_sp and ss_size should be ignored in this case.
289                ss_size: sigstack_size,
290            };
291            // SAFETY: we warned the caller this disables the alternate signal stack!
292            unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) };
293            // SAFETY: We know from `get_stackp` that the alternate stack we installed is part of
294            // a mapping that started one page earlier, so walk back a page and unmap from there.
295            unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };
296        }
297
298        delete_current_info();
299    }
300
301    /// Modern kernels on modern hardware can have dynamic signal stack sizes.
302    #[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
303    fn sigstack_size() -> usize {
304        let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) };
305        // If getauxval couldn't find the entry, it returns 0,
306        // so take the higher of the "constant" and auxval.
307        // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ
308        libc::SIGSTKSZ.max(dynamic_sigstksz as _)
309    }
310
311    /// Not all OS support hardware where this is needed.
312    #[cfg(not(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc"))))]
313    fn sigstack_size() -> usize {
314        libc::SIGSTKSZ
315    }
316
317    #[cfg(any(target_os = "solaris", target_os = "illumos"))]
318    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
319        let mut current_stack: libc::stack_t = crate::mem::zeroed();
320        assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
321        Some(current_stack.ss_sp)
322    }
323
324    #[cfg(target_os = "macos")]
325    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
326        let th = libc::pthread_self();
327        let stackptr = libc::pthread_get_stackaddr_np(th);
328        Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th)))
329    }
330
331    #[cfg(target_os = "openbsd")]
332    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
333        let mut current_stack: libc::stack_t = crate::mem::zeroed();
334        assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
335
336        let stack_ptr = current_stack.ss_sp;
337        let stackaddr = if libc::pthread_main_np() == 1 {
338            // main thread
339            stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
340        } else {
341            // new thread
342            stack_ptr.addr() - current_stack.ss_size
343        };
344        Some(stack_ptr.with_addr(stackaddr))
345    }
346
347    #[cfg(any(
348        target_os = "android",
349        target_os = "freebsd",
350        target_os = "netbsd",
351        target_os = "hurd",
352        target_os = "linux",
353        target_os = "l4re"
354    ))]
355    unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
356        use crate::pin::pin;
357        use crate::sys::helpers::COpaque;
358
359        let mut ret = None;
360        let mut attr: COpaque<libc::pthread_attr_t> = COpaque::uninit();
361        if !falsecfg!(target_os = "freebsd") {
362            attr = COpaque::zeroed();
363        }
364        let attr = {
    super let mut pinned: ::core::pin::PinMacroHelper<_> =
        ::core::pin::PinMacroHelper { value: attr };
    unsafe { ::core::pin::pin_new_unchecked_in_helper(&mut pinned) }
}pin!(attr);
365        // FIXME(pin-ergonomics): remove the next line.
366        let attr = attr.into_ref();
367
368        #[cfg(target_os = "freebsd")]
369        assert_eq!(libc::pthread_attr_init(attr.get()), 0);
370        #[cfg(target_os = "freebsd")]
371        let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.get());
372        #[cfg(not(target_os = "freebsd"))]
373        let e = libc::pthread_getattr_np(libc::pthread_self(), attr.get());
374        if e == 0 {
375            let mut stackaddr = crate::ptr::null_mut();
376            let mut stacksize = 0;
377            {
    match (&libc::pthread_attr_getstack(attr.get(), &mut stackaddr,
                    &mut stacksize), &0) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(libc::pthread_attr_getstack(attr.get(), &mut stackaddr, &mut stacksize), 0);
378            ret = Some(stackaddr);
379        }
380        if e == 0 || falsecfg!(target_os = "freebsd") {
381            {
    match (&libc::pthread_attr_destroy(attr.get()), &0) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(libc::pthread_attr_destroy(attr.get()), 0);
382        }
383        ret
384    }
385
386    fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> {
387        let stackptr = unsafe { get_stack_start()? };
388        let stackaddr = stackptr.addr();
389
390        // Ensure stackaddr is page aligned! A parent process might
391        // have reset RLIMIT_STACK to be non-page aligned. The
392        // pthread_attr_getstack() reports the usable stack area
393        // stackaddr < stackaddr + stacksize, so if stackaddr is not
394        // page-aligned, calculate the fix such that stackaddr <
395        // new_page_aligned_stackaddr < stackaddr + stacksize
396        let remainder = stackaddr % page_size;
397        Some(if remainder == 0 {
398            stackptr
399        } else {
400            stackptr.with_addr(stackaddr + page_size - remainder)
401        })
402    }
403
404    #[forbid(unsafe_op_in_unsafe_fn)]
405    unsafe fn install_main_guard() -> Option<Range<usize>> {
406        let page_size = PAGE_SIZE.load(Ordering::Relaxed);
407
408        unsafe {
409            // this way someone on any unix-y OS can check that all these compile
410            if truecfg!(all(target_os = "linux", not(target_env = "musl"))) {
411                install_main_guard_linux(page_size)
412            } else if falsecfg!(all(target_os = "linux", target_env = "musl")) {
413                install_main_guard_linux_musl(page_size)
414            } else if falsecfg!(target_os = "freebsd") {
415                #[cfg(not(target_os = "freebsd"))]
416                return None;
417                // The FreeBSD code cannot be checked on non-BSDs.
418                #[cfg(target_os = "freebsd")]
419                install_main_guard_freebsd(page_size)
420            } else if falsecfg!(any(target_os = "netbsd", target_os = "openbsd")) {
421                install_main_guard_bsds(page_size)
422            } else {
423                install_main_guard_default(page_size)
424            }
425        }
426    }
427
428    #[forbid(unsafe_op_in_unsafe_fn)]
429    unsafe fn install_main_guard_linux(page_size: usize) -> Option<Range<usize>> {
430        // See the corresponding conditional in init().
431        // Avoid stack_start_aligned, which makes slow syscalls to read /proc/self/maps
432        if falsecfg!(panic = "immediate-abort") {
433            return None;
434        }
435        // Linux doesn't allocate the whole stack right away, and
436        // the kernel has its own stack-guard mechanism to fault
437        // when growing too close to an existing mapping. If we map
438        // our own guard, then the kernel starts enforcing a rather
439        // large gap above that, rendering much of the possible
440        // stack space useless. See #43052.
441        //
442        // Instead, we'll just note where we expect rlimit to start
443        // faulting, so our handler can report "stack overflow", and
444        // trust that the kernel's own stack guard will work.
445        let stackptr = stack_start_aligned(page_size)?;
446        let stackaddr = stackptr.addr();
447        Some(stackaddr - page_size..stackaddr)
448    }
449
450    #[forbid(unsafe_op_in_unsafe_fn)]
451    unsafe fn install_main_guard_linux_musl(_page_size: usize) -> Option<Range<usize>> {
452        // For the main thread, the musl's pthread_attr_getstack
453        // returns the current stack size, rather than maximum size
454        // it can eventually grow to. It cannot be used to determine
455        // the position of kernel's stack guard.
456        None
457    }
458
459    #[forbid(unsafe_op_in_unsafe_fn)]
460    #[cfg(target_os = "freebsd")]
461    unsafe fn install_main_guard_freebsd(page_size: usize) -> Option<Range<usize>> {
462        // See the corresponding conditional in install_main_guard_linux().
463        if cfg!(panic = "immediate-abort") {
464            return None;
465        }
466        // FreeBSD's stack autogrows, and optionally includes a guard page
467        // at the bottom. If we try to remap the bottom of the stack
468        // ourselves, FreeBSD's guard page moves upwards. So we'll just use
469        // the builtin guard page.
470        let stackptr = stack_start_aligned(page_size)?;
471        let guardaddr = stackptr.addr();
472        // Technically the number of guard pages is tunable and controlled
473        // by the security.bsd.stack_guard_page sysctl.
474        // By default it is 1, checking once is enough since it is
475        // a boot time config value.
476        static PAGES: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new();
477
478        let pages = PAGES.get_or_init(|| {
479            let mut guard: usize = 0;
480            let mut size = size_of_val(&guard);
481            let oid = c"security.bsd.stack_guard_page";
482
483            let r = unsafe {
484                libc::sysctlbyname(
485                    oid.as_ptr(),
486                    (&raw mut guard).cast(),
487                    &raw mut size,
488                    ptr::null_mut(),
489                    0,
490                )
491            };
492            if r == 0 { guard } else { 1 }
493        });
494        Some(guardaddr..guardaddr + pages * page_size)
495    }
496
497    #[forbid(unsafe_op_in_unsafe_fn)]
498    unsafe fn install_main_guard_bsds(page_size: usize) -> Option<Range<usize>> {
499        // See the corresponding conditional in install_main_guard_linux().
500        if falsecfg!(panic = "immediate-abort") {
501            return None;
502        }
503        // OpenBSD stack already includes a guard page, and stack is
504        // immutable.
505        // NetBSD stack includes the guard page.
506        //
507        // We'll just note where we expect rlimit to start
508        // faulting, so our handler can report "stack overflow", and
509        // trust that the kernel's own stack guard will work.
510        let stackptr = stack_start_aligned(page_size)?;
511        let stackaddr = stackptr.addr();
512        Some(stackaddr - page_size..stackaddr)
513    }
514
515    #[forbid(unsafe_op_in_unsafe_fn)]
516    unsafe fn install_main_guard_default(page_size: usize) -> Option<Range<usize>> {
517        // Reallocate the last page of the stack.
518        // This ensures SIGBUS will be raised on
519        // stack overflow.
520        // Systems which enforce strict PAX MPROTECT do not allow
521        // to mprotect() a mapping with less restrictive permissions
522        // than the initial mmap() used, so we mmap() here with
523        // read/write permissions and only then mprotect() it to
524        // no permissions at all. See issue #50313.
525        let stackptr = stack_start_aligned(page_size)?;
526        let result = unsafe {
527            mmap64(
528                stackptr,
529                page_size,
530                PROT_READ | PROT_WRITE,
531                MAP_PRIVATE | MAP_ANON | MAP_FIXED,
532                -1,
533                0,
534            )
535        };
536        if result != stackptr || result == MAP_FAILED {
537            {
    ::core::panicking::panic_fmt(format_args!("failed to allocate a guard page: {0}",
            io::Error::last_os_error()));
};panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
538        }
539
540        let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) };
541        if result != 0 {
542            {
    ::core::panicking::panic_fmt(format_args!("failed to protect the guard page: {0}",
            io::Error::last_os_error()));
};panic!("failed to protect the guard page: {}", io::Error::last_os_error());
543        }
544
545        let guardaddr = stackptr.addr();
546
547        Some(guardaddr..guardaddr + page_size)
548    }
549
550    #[cfg(any(
551        target_os = "macos",
552        target_os = "openbsd",
553        target_os = "solaris",
554        target_os = "illumos",
555    ))]
556    // FIXME: I am probably not unsafe.
557    unsafe fn current_guard() -> Option<Range<usize>> {
558        let stackptr = get_stack_start()?;
559        let stackaddr = stackptr.addr();
560        Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
561    }
562
563    #[cfg(any(
564        target_os = "android",
565        target_os = "freebsd",
566        target_os = "hurd",
567        target_os = "linux",
568        target_os = "netbsd",
569        target_os = "l4re"
570    ))]
571    // FIXME: I am probably not unsafe.
572    unsafe fn current_guard() -> Option<Range<usize>> {
573        use crate::pin::pin;
574        use crate::sys::helpers::COpaque;
575
576        let mut ret = None;
577
578        let mut attr: COpaque<libc::pthread_attr_t> = COpaque::uninit();
579        if !falsecfg!(target_os = "freebsd") {
580            attr = COpaque::zeroed();
581        }
582        let attr = {
    super let mut pinned: ::core::pin::PinMacroHelper<_> =
        ::core::pin::PinMacroHelper { value: attr };
    unsafe { ::core::pin::pin_new_unchecked_in_helper(&mut pinned) }
}pin!(attr);
583        // FIXME(pin-ergonomics): remove the next line.
584        let attr = attr.into_ref();
585
586        #[cfg(target_os = "freebsd")]
587        assert_eq!(libc::pthread_attr_init(attr.get()), 0);
588        #[cfg(target_os = "freebsd")]
589        let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.get());
590        #[cfg(not(target_os = "freebsd"))]
591        let e = libc::pthread_getattr_np(libc::pthread_self(), attr.get());
592        if e == 0 {
593            let mut guardsize = 0;
594            {
    match (&libc::pthread_attr_getguardsize(attr.get(), &mut guardsize), &0) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(libc::pthread_attr_getguardsize(attr.get(), &mut guardsize), 0);
595            if guardsize == 0 {
596                if falsecfg!(all(target_os = "linux", target_env = "musl")) {
597                    // musl versions before 1.1.19 always reported guard
598                    // size obtained from pthread_attr_get_np as zero.
599                    // Use page size as a fallback.
600                    guardsize = PAGE_SIZE.load(Ordering::Relaxed);
601                } else {
602                    { ::core::panicking::panic_fmt(format_args!("there is no guard page")); };panic!("there is no guard page");
603                }
604            }
605            let mut stackptr = crate::ptr::null_mut::<libc::c_void>();
606            let mut size = 0;
607            {
    match (&libc::pthread_attr_getstack(attr.get(), &mut stackptr, &mut size),
            &0) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(libc::pthread_attr_getstack(attr.get(), &mut stackptr, &mut size), 0);
608
609            let stackaddr = stackptr.addr();
610            ret = if falsecfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) {
611                Some(stackaddr - guardsize..stackaddr)
612            } else if falsecfg!(all(target_os = "linux", target_env = "musl")) {
613                Some(stackaddr - guardsize..stackaddr)
614            } else if truecfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
615            {
616                // glibc used to include the guard area within the stack, as noted in the BUGS
617                // section of `man pthread_attr_getguardsize`. This has been corrected starting
618                // with glibc 2.27, and in some distro backports, so the guard is now placed at the
619                // end (below) the stack. There's no easy way for us to know which we have at
620                // runtime, so we'll just match any fault in the range right above or below the
621                // stack base to call that fault a stack overflow.
622                Some(stackaddr - guardsize..stackaddr + guardsize)
623            } else {
624                Some(stackaddr..stackaddr + guardsize)
625            };
626        }
627        if e == 0 || falsecfg!(target_os = "freebsd") {
628            {
    match (&libc::pthread_attr_destroy(attr.get()), &0) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(libc::pthread_attr_destroy(attr.get()), 0);
629        }
630        ret
631    }
632}
633
634// This is intentionally not enabled on iOS/tvOS/watchOS/visionOS, as it uses
635// several symbols that might lead to rejections from the App Store, namely
636// `sigaction`, `sigaltstack`, `sysctlbyname`, `mmap`, `munmap` and `mprotect`.
637//
638// This might be overly cautious, though it is also what Swift does (and they
639// usually have fewer qualms about forwards compatibility, since the runtime
640// is shipped with the OS):
641// <https://github.com/apple/swift/blob/swift-5.10-RELEASE/stdlib/public/runtime/CrashHandlerMacOS.cpp>
642#[cfg(any(
643    miri,
644    not(any(
645        target_os = "linux",
646        target_os = "freebsd",
647        target_os = "hurd",
648        target_os = "macos",
649        target_os = "netbsd",
650        target_os = "openbsd",
651        target_os = "solaris",
652        target_os = "illumos",
653        target_os = "cygwin",
654    ))
655))]
656mod imp {
657    pub unsafe fn init() {}
658
659    pub unsafe fn make_handler(_main_thread: bool) -> super::Handler {
660        super::Handler::null()
661    }
662
663    pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
664}
665
666#[cfg(target_os = "cygwin")]
667mod imp {
668    mod c {
669        pub type PVECTORED_EXCEPTION_HANDLER =
670            Option<unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32>;
671        pub type NTSTATUS = i32;
672        pub type BOOL = i32;
673
674        unsafe extern "system" {
675            pub fn AddVectoredExceptionHandler(
676                first: u32,
677                handler: PVECTORED_EXCEPTION_HANDLER,
678            ) -> *mut core::ffi::c_void;
679            pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL;
680        }
681
682        pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _;
683        pub const EXCEPTION_CONTINUE_SEARCH: i32 = 1i32;
684
685        #[repr(C)]
686        #[derive(Clone, Copy)]
687        pub struct EXCEPTION_POINTERS {
688            pub ExceptionRecord: *mut EXCEPTION_RECORD,
689            // We don't need this field here
690            // pub Context: *mut CONTEXT,
691        }
692        #[repr(C)]
693        #[derive(Clone, Copy)]
694        pub struct EXCEPTION_RECORD {
695            pub ExceptionCode: NTSTATUS,
696            pub ExceptionFlags: u32,
697            pub ExceptionRecord: *mut EXCEPTION_RECORD,
698            pub ExceptionAddress: *mut core::ffi::c_void,
699            pub NumberParameters: u32,
700            pub ExceptionInformation: [usize; 15],
701        }
702    }
703
704    /// Reserve stack space for use in stack overflow exceptions.
705    fn reserve_stack() {
706        let result = unsafe { c::SetThreadStackGuarantee(&mut 0x5000) };
707        // Reserving stack space is not critical so we allow it to fail in the released build of libstd.
708        // We still use debug assert here so that CI will test that we haven't made a mistake calling the function.
709        debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling");
710    }
711
712    unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> i32 {
713        // SAFETY: It's up to the caller (which in this case is the OS) to ensure that `ExceptionInfo` is valid.
714        unsafe {
715            let rec = &(*(*ExceptionInfo).ExceptionRecord);
716            let code = rec.ExceptionCode;
717
718            if code == c::EXCEPTION_STACK_OVERFLOW {
719                crate::thread::with_current_name(|name| {
720                    let name = name.unwrap_or("<unknown>");
721                    let tid = crate::thread::current_os_id();
722                    rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n");
723                });
724            }
725            c::EXCEPTION_CONTINUE_SEARCH
726        }
727    }
728
729    pub unsafe fn init() {
730        // SAFETY: `vectored_handler` has the correct ABI and is safe to call during exception handling.
731        unsafe {
732            let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler));
733            // Similar to the above, adding the stack overflow handler is allowed to fail
734            // but a debug assert is used so CI will still test that it normally works.
735            debug_assert!(!result.is_null(), "failed to install exception handler");
736        }
737        // Set the thread stack guarantee for the main thread.
738        reserve_stack();
739    }
740
741    pub unsafe fn make_handler(main_thread: bool) -> super::Handler {
742        if !main_thread {
743            reserve_stack();
744        }
745        super::Handler::null()
746    }
747
748    pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
749}