Skip to main content

std/sys/pal/unix/stack_overflow/
handler_signal.rs

1use libc::{
2    MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK,
3    SA_SIGINFO, SIG_DFL, SIGBUS, SIGSEGV, SS_DISABLE, sigaction, sigaltstack, sighandler_t,
4};
5#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
6use libc::{mmap as mmap64, mprotect, munmap};
7#[cfg(all(target_os = "linux", target_env = "gnu"))]
8use libc::{mmap64, mprotect, munmap};
9
10use super::Handler;
11use super::thread_info::{delete_current_info, set_current_info, with_current_info};
12use crate::ops::Range;
13use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
14use crate::sys::pal::unix::conf;
15use crate::{io, mem, ptr};
16
17/// Signal handler for the SIGSEGV and SIGBUS handlers.
18///
19/// We've got guard pages (unmapped pages) at the end of every thread's
20/// stack, so if a thread ends up running into the guard page it'll trigger
21/// this handler. We want to detect these cases and print out a helpful error
22/// saying that the stack has overflowed. All other signals, however, should
23/// go back to what they were originally supposed to do.
24///
25/// This handler currently exists purely to print an informative message
26/// whenever a thread overflows its stack. We then abort to exit and
27/// indicate a crash, but to avoid a misleading SIGSEGV that might lead
28/// users to believe that unsafe code has accessed an invalid pointer; the
29/// SIGSEGV encountered when overflowing the stack is expected and
30/// well-defined.
31///
32/// If this is not a stack overflow, the handler un-registers itself and
33/// then returns (to allow the original signal to be delivered again).
34/// Returning from this kind of signal handler is technically not defined
35/// to work when reading the POSIX spec strictly, but in practice it turns
36/// out many large systems and all implementations allow returning from a
37/// signal handler to work. For a more detailed explanation see the
38/// comments on #26458.
39///
40/// # Safety
41/// Rust doesn't call this, it *gets called* by the kernel, which we expect
42/// to provide valid parameters. Apart from that, this function does not
43/// have any other preconditions.
44unsafe extern "C" fn signal_handler(
45    signum: libc::c_int,
46    info: *mut libc::siginfo_t,
47    _data: *mut libc::c_void,
48) {
49    // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`.
50    let fault_addr = unsafe { (*info).si_addr().addr() };
51
52    // `with_current_info` expects that the process aborts after it is
53    // called. If the signal was not caused by a memory access, this might
54    // not be true. We detect this by noticing that the `si_addr` field is
55    // zero if the signal is synthetic.
56    if fault_addr != 0 {
57        with_current_info(|thread_info| {
58            // If the faulting address is within the guard page, then we print a
59            // message saying so and abort.
60            if let Some(thread_info) = thread_info
61                && thread_info.guard_page_range.contains(&fault_addr)
62            {
63                // Hey you! Yes, you modifying the stack overflow message!
64                // Please make sure that all functions called here are
65                // actually async-signal-safe. If they're not, try retrieving
66                // the information beforehand and storing it in `ThreadInfo`.
67                // Thank you!
68                // - says Jonas after having had to watch his carefully
69                //   written code get made unsound again.
70                let tid = thread_info.tid;
71                let name = thread_info.name.as_deref().unwrap_or("<unknown>");
72                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");
73                {
    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");
74            }
75        })
76    }
77
78    // Unregister ourselves by reverting back to the default behavior.
79    // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
80    let mut action: sigaction = unsafe { mem::zeroed() };
81    action.sa_sigaction = SIG_DFL;
82    // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
83    unsafe { sigaction(signum, &action, ptr::null_mut()) };
84
85    // See comment above for why this function returns.
86}
87
88static PAGE_SIZE: Atomic<usize> = AtomicUsize::new(0);
89// Store a pointer to the allocation for the main thread's altstack so that
90// tools like valgrind don't complain about a leaked unreachable allocation.
91//
92// If the main thread exits, the process will terminate so there's no use in
93// freeing resources. It also means that the altstack is still installed
94// while TLS destructors are run on the main thread (c.f. #111272).
95static MAIN_ALTSTACK: Atomic<*mut libc::c_void> = AtomicPtr::new(ptr::null_mut());
96static NEED_ALTSTACK: Atomic<bool> = AtomicBool::new(false);
97
98/// # Safety
99/// Must be called only once, on the main thread, during program startup.
100pub unsafe fn init() {
101    PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed);
102
103    // SAFETY:
104    // This is only called on the main thread, and since it is still early
105    // in the programs lifetime there is (almost) certainly enough stack
106    // space left to install the guard page.
107    let mut guard_page_range = unsafe { install_main_guard() };
108
109    // Even for panic=immediate-abort, installing the guard pages is important for soundness.
110    // That said, we do not care about giving nice stackoverflow messages via our custom
111    // signal handler, just exit early and let the user enjoy the segfault.
112    if falsecfg!(panic = "immediate-abort") {
113        return;
114    }
115
116    // SAFETY: C structures are always zero-initializable.
117    let mut action: sigaction = unsafe { mem::zeroed() };
118    for &signal in &[SIGSEGV, SIGBUS] {
119        // SAFETY: just fetches the current signal handler into action
120        unsafe { sigaction(signal, ptr::null_mut(), &mut action) };
121        // We assume that overriding the signal handler is always safe,
122        // which might conflict with certain libraries that rely on a
123        // specific signal behaviour. To prevent problems, we only
124        // override the handler if it has not been set yet.
125        if action.sa_sigaction == SIG_DFL {
126            if !NEED_ALTSTACK.load(Ordering::Relaxed) {
127                // haven't set up our sigaltstack yet
128                NEED_ALTSTACK.store(true, Ordering::Release);
129                let handler = make_handler(true);
130                MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
131                mem::forget(handler);
132
133                if let Some(guard_page_range) = guard_page_range.take() {
134                    set_current_info(guard_page_range);
135                }
136            }
137
138            action.sa_flags = SA_SIGINFO | SA_ONSTACK;
139            action.sa_sigaction = signal_handler
140                as unsafe extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)
141                as sighandler_t;
142            // SAFETY:
143            // `&action` describes a valid `sigaction` and `signal_handler`
144            // is safe to use as a signal handler for `SIGSEGV` and `SIGBUS`.
145            unsafe { sigaction(signal, &action, ptr::null_mut()) };
146        }
147    }
148}
149
150fn get_stack() -> libc::stack_t {
151    // OpenBSD requires this flag for stack mapping
152    // otherwise the said mapping will fail as a no-op on most systems
153    // and has a different meaning on FreeBSD
154    #[cfg(any(
155        target_os = "openbsd",
156        target_os = "netbsd",
157        target_os = "linux",
158        target_os = "dragonfly",
159    ))]
160    let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
161    #[cfg(not(any(
162        target_os = "openbsd",
163        target_os = "netbsd",
164        target_os = "linux",
165        target_os = "dragonfly",
166    )))]
167    let flags = MAP_PRIVATE | MAP_ANON;
168
169    let sigstack_size = sigstack_size();
170    let page_size = PAGE_SIZE.load(Ordering::Relaxed);
171
172    // SAFETY: this does not unmap any existing pages.
173    let stackp = unsafe {
174        mmap64(ptr::null_mut(), sigstack_size + page_size, PROT_READ | PROT_WRITE, flags, -1, 0)
175    };
176    if stackp == MAP_FAILED {
177        {
    ::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());
178    }
179    // SAFETY: this only affects the memory we just allocated.
180    let guard_result = unsafe { libc::mprotect(stackp, page_size, PROT_NONE) };
181    if guard_result != 0 {
182        {
    ::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());
183    }
184    // SAFETY:
185    // The region was allocated with a larger size than `page_size`, so this
186    // addition is within bounds.
187    let stackp = unsafe { stackp.add(page_size) };
188
189    libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size }
190}
191
192pub fn make_handler(main_thread: bool) -> Handler {
193    if falsecfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) {
194        return Handler::null();
195    }
196
197    if !main_thread {
198        if let Some(guard_page_range) = current_guard() {
199            set_current_info(guard_page_range);
200        }
201    }
202
203    // Load the current alternate signal stack to see if we need to install
204    // our own.
205    //
206    // SAFETY: C structures are always zero-initializable.
207    let mut stack = unsafe { mem::zeroed() };
208    // SAFETY: `&mut stack` is valid for writing a `stack_t`.
209    unsafe { sigaltstack(ptr::null(), &mut stack) };
210
211    // Configure alternate signal stack, if one is not already set.
212    if stack.ss_flags & SS_DISABLE != 0 {
213        let stack = get_stack();
214        // SAFETY:
215        // `stack_t` is a freshly allocated stack that's not used anywhere
216        // else. It contains a guard page, so stack overflows in signal
217        // handlers will not cause undefined behaviour. We must make the
218        // fundamental runtime assumption that it is safe to install an
219        // alternate signal stack if there is none currently installed.
220        // This might conflict with foreign libraries that use the existence
221        // of an alternate signal stack as indication that certain runtime
222        // initialisation by the library has been performed (e.g. old
223        // versions of `std` assumed that certain thread-locals were already
224        // accessed and thus initialized in the thread if the stack overflow
225        // signal was successfully delivered). Such assumptions in other
226        // libraries are fundamentally flawed, so we pay no regard to them.
227        unsafe { sigaltstack(&stack, ptr::null_mut()) };
228        Handler { data: stack.ss_sp as *mut libc::c_void }
229    } else {
230        Handler::null()
231    }
232}
233
234/// # Safety
235/// Must only be called with a pointer returned by `make_handler`, and only
236/// once per `Handler`.
237pub unsafe fn drop_handler(data: *mut libc::c_void) {
238    if !data.is_null() {
239        let sigstack_size = sigstack_size();
240        let page_size = PAGE_SIZE.load(Ordering::Relaxed);
241        let disabling_stack = libc::stack_t {
242            ss_sp: ptr::null_mut(),
243            ss_flags: SS_DISABLE,
244            // Workaround for bug in macOS implementation of sigaltstack
245            // UNIX2003 which returns ENOMEM when disabling a stack while
246            // passing ss_size smaller than MINSIGSTKSZ. According to POSIX
247            // both ss_sp and ss_size should be ignored in this case.
248            ss_size: sigstack_size,
249        };
250        // SAFETY:
251        // We assume that disabling the alternate signal stack is always
252        // sound, even if the current alternate signal stack is not the one
253        // we installed in `make_handler`. Any stack overflows from this
254        // point on will abort the program when the kernel tries to write
255        // the signal information to the guard page.
256        //
257        // FIXME: detect if the stack has changed, and only uninstall if it hasn't.
258        unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) };
259        // The stack returned by `get_stack` is part of a mapping that
260        // started one page earlier, so walk back a page and unmap from
261        // there.
262        //
263        // SAFETY:
264        // This allocation was created by us in `get_stack` and, as the
265        // alternate signal stack is now disabled, is no longer in use.
266        unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };
267    }
268
269    delete_current_info();
270}
271
272/// Modern kernels on modern hardware can have dynamic signal stack sizes.
273#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
274fn sigstack_size() -> usize {
275    // SAFETY: `getauxval` is always safe to call.
276    let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) };
277    // If getauxval couldn't find the entry, it returns 0,
278    // so take the higher of the "constant" and auxval.
279    // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ
280    libc::SIGSTKSZ.max(dynamic_sigstksz as _)
281}
282
283/// Not all OS support hardware where this is needed.
284#[cfg(not(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc"))))]
285fn sigstack_size() -> usize {
286    libc::SIGSTKSZ
287}
288
289#[cfg(any(target_os = "solaris", target_os = "illumos"))]
290fn get_stack_start() -> Option<*mut libc::c_void> {
291    // SAFETY: C types are always zero-initializable.
292    let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() };
293    // SAFETY:
294    // The pointer is valid for writing a `stack_t`.
295    assert_eq!(unsafe { libc::stack_getbounds(&mut current_stack) }, 0);
296    Some(current_stack.ss_sp)
297}
298
299#[cfg(target_os = "macos")]
300fn get_stack_start() -> Option<*mut libc::c_void> {
301    // SAFETY: always safe to call.
302    let th = unsafe { libc::pthread_self() };
303    // SAFETY: `th` is a valid `pthread_t`.
304    unsafe {
305        let stackptr = libc::pthread_get_stackaddr_np(th);
306        let stacksize = libc::pthread_get_stacksize_np(th);
307        Some(stackptr.map_addr(|addr| addr - stacksize))
308    }
309}
310
311#[cfg(target_os = "openbsd")]
312fn get_stack_start() -> Option<*mut libc::c_void> {
313    // SAFETY: C types are always zero-initializable.
314    let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() };
315    // SAFETY:
316    // * calling `pthread_self` is always valid and returns a valid `pthread_t`.
317    // * `&mut current_stack` is coerced to a pointer that is valid for writing
318    //   a `stack_t`.
319    assert_eq!(unsafe { libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack) }, 0);
320
321    let stack_ptr = current_stack.ss_sp;
322    // SAFETY: this is always safe to call.
323    let stackaddr = if unsafe { libc::pthread_main_np() } == 1 {
324        // main thread
325        stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
326    } else {
327        // new thread
328        stack_ptr.addr() - current_stack.ss_size
329    };
330    Some(stack_ptr.with_addr(stackaddr))
331}
332
333#[cfg(any(
334    target_os = "android",
335    target_os = "freebsd",
336    target_os = "netbsd",
337    target_os = "hurd",
338    target_os = "linux",
339    target_os = "l4re"
340))]
341fn get_stack_start() -> Option<*mut libc::c_void> {
342    use crate::pin::pin;
343    use crate::sys::helpers::COpaque;
344
345    let mut ret = None;
346    let mut attr: COpaque<libc::pthread_attr_t> = COpaque::uninit();
347    if !falsecfg!(target_os = "freebsd") {
348        attr = COpaque::zeroed();
349    }
350    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);
351    // FIXME(pin-ergonomics): remove the next line.
352    let attr = attr.into_ref();
353
354    // SAFETY:
355    // The attributes object has not been initialized yet and will not be
356    // moved until destroyed.
357    #[cfg(target_os = "freebsd")]
358    assert_eq!(unsafe { libc::pthread_attr_init(attr.get()) }, 0);
359    // SAFETY:
360    // * calling `pthread_self` is always valid and returns a valid `pthread_t`
361    // * `attr` is an initialized attribute object that can be written to.
362    #[cfg(target_os = "freebsd")]
363    let e = unsafe { libc::pthread_attr_get_np(libc::pthread_self(), attr.get()) };
364    // SAFETY:
365    // * calling `pthread_self` is always valid and returns a valid `pthread_t`
366    // * `attr` can be written to, and will be initialized by this call.
367    #[cfg(not(target_os = "freebsd"))]
368    let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) };
369    if e == 0 {
370        let mut stackaddr = crate::ptr::null_mut();
371        let mut stacksize = 0;
372        // SAFETY:
373        // `attr` is an initialized attribute object and both the pointers
374        // are valid for writing.
375        {
    match (&unsafe {
                    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!(
376            unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackaddr, &mut stacksize) },
377            0
378        );
379        ret = Some(stackaddr);
380    }
381    if e == 0 || falsecfg!(target_os = "freebsd") {
382        // SAFETY:
383        // `attr` was initialized either by `pthread_attr_init` (FreeBSD) or
384        // by `pthread_attr_get_np`, and is not used after this point.
385        {
    match (&unsafe { 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!(unsafe { libc::pthread_attr_destroy(attr.get()) }, 0);
386    }
387    ret
388}
389
390fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> {
391    let stackptr = get_stack_start()?;
392    let stackaddr = stackptr.addr();
393
394    // Ensure stackaddr is page aligned! A parent process might
395    // have reset RLIMIT_STACK to be non-page aligned. The
396    // pthread_attr_getstack() reports the usable stack area
397    // stackaddr < stackaddr + stacksize, so if stackaddr is not
398    // page-aligned, calculate the fix such that stackaddr <
399    // new_page_aligned_stackaddr < stackaddr + stacksize
400    let remainder = stackaddr % page_size;
401    Some(if remainder == 0 {
402        stackptr
403    } else {
404        stackptr.with_addr(stackaddr + page_size - remainder)
405    })
406}
407
408/// # Safety
409/// This function must only be called from the main thread, and there must
410/// be sufficient stack space remaining to place a stack guard.
411unsafe fn install_main_guard() -> Option<Range<usize>> {
412    let page_size = PAGE_SIZE.load(Ordering::Relaxed);
413
414    // this way someone on any unix-y OS can check that all these compile
415    if truecfg!(all(target_os = "linux", not(target_env = "musl"))) {
416        install_main_guard_linux(page_size)
417    } else if falsecfg!(all(target_os = "linux", target_env = "musl")) {
418        install_main_guard_linux_musl(page_size)
419    } else if falsecfg!(target_os = "freebsd") {
420        #[cfg(not(target_os = "freebsd"))]
421        return None;
422        // The FreeBSD code cannot be checked on non-BSDs.
423        #[cfg(target_os = "freebsd")]
424        install_main_guard_freebsd(page_size)
425    } else if falsecfg!(any(target_os = "netbsd", target_os = "openbsd")) {
426        install_main_guard_bsds(page_size)
427    } else {
428        // SAFETY: guaranteed by caller.
429        unsafe { install_main_guard_default(page_size) }
430    }
431}
432
433fn install_main_guard_linux(page_size: usize) -> Option<Range<usize>> {
434    // See the corresponding conditional in init().
435    // Avoid stack_start_aligned, which makes slow syscalls to read /proc/self/maps
436    if falsecfg!(panic = "immediate-abort") {
437        return None;
438    }
439    // Linux doesn't allocate the whole stack right away, and
440    // the kernel has its own stack-guard mechanism to fault
441    // when growing too close to an existing mapping. If we map
442    // our own guard, then the kernel starts enforcing a rather
443    // large gap above that, rendering much of the possible
444    // stack space useless. See #43052.
445    //
446    // Instead, we'll just note where we expect rlimit to start
447    // faulting, so our handler can report "stack overflow", and
448    // trust that the kernel's own stack guard will work.
449    let stackptr = stack_start_aligned(page_size)?;
450    let stackaddr = stackptr.addr();
451    Some(stackaddr - page_size..stackaddr)
452}
453
454fn install_main_guard_linux_musl(_page_size: usize) -> Option<Range<usize>> {
455    // For the main thread, the musl's pthread_attr_getstack
456    // returns the current stack size, rather than maximum size
457    // it can eventually grow to. It cannot be used to determine
458    // the position of kernel's stack guard.
459    None
460}
461
462#[cfg(target_os = "freebsd")]
463fn install_main_guard_freebsd(page_size: usize) -> Option<Range<usize>> {
464    // See the corresponding conditional in install_main_guard_linux().
465    if cfg!(panic = "immediate-abort") {
466        return None;
467    }
468    // FreeBSD's stack autogrows, and optionally includes a guard page
469    // at the bottom. If we try to remap the bottom of the stack
470    // ourselves, FreeBSD's guard page moves upwards. So we'll just use
471    // the builtin guard page.
472    let stackptr = stack_start_aligned(page_size)?;
473    let guardaddr = stackptr.addr();
474
475    // The number of guard pages is tunable and controlled by the
476    // security.bsd.stack_guard_page sysctl. By default it is 1,
477    // checking once is enough since it is a boot time config value.
478    let mut guard: usize = 0;
479    let mut size = size_of_val(&guard);
480    let oid = c"security.bsd.stack_guard_page";
481    let r = unsafe {
482        libc::sysctlbyname(oid.as_ptr(), (&raw mut guard).cast(), &raw mut size, ptr::null_mut(), 0)
483    };
484    let pages = if r == 0 { guard } else { 1 };
485
486    Some(guardaddr..guardaddr + pages * page_size)
487}
488
489fn install_main_guard_bsds(page_size: usize) -> Option<Range<usize>> {
490    // See the corresponding conditional in install_main_guard_linux().
491    if falsecfg!(panic = "immediate-abort") {
492        return None;
493    }
494    // OpenBSD stack already includes a guard page, and stack is
495    // immutable.
496    // NetBSD stack includes the guard page.
497    //
498    // We'll just note where we expect rlimit to start
499    // faulting, so our handler can report "stack overflow", and
500    // trust that the kernel's own stack guard will work.
501    let stackptr = stack_start_aligned(page_size)?;
502    let stackaddr = stackptr.addr();
503    Some(stackaddr - page_size..stackaddr)
504}
505
506/// # Safety
507/// This function must only be called from the main thread, and there must
508/// be sufficient stack space remaining to place a stack guard.
509unsafe fn install_main_guard_default(page_size: usize) -> Option<Range<usize>> {
510    // Reallocate the last page of the stack.
511    // This ensures SIGBUS will be raised on
512    // stack overflow.
513    // Systems which enforce strict PAX MPROTECT do not allow
514    // to mprotect() a mapping with less restrictive permissions
515    // than the initial mmap() used, so we mmap() here with
516    // read/write permissions and only then mprotect() it to
517    // no permissions at all. See issue #50313.
518    let stackptr = stack_start_aligned(page_size)?;
519    // SAFETY:
520    // The memory region from `stackptr..stackptr + page_size` belongs to
521    // the current thread's stack, and the caller has asserted that there
522    // is sufficient stack space, which means that this will not overwrite
523    // any existing allocations.
524    let result = unsafe {
525        mmap64(
526            stackptr,
527            page_size,
528            PROT_READ | PROT_WRITE,
529            MAP_PRIVATE | MAP_ANON | MAP_FIXED,
530            -1,
531            0,
532        )
533    };
534    if result != stackptr || result == MAP_FAILED {
535        {
    ::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());
536    }
537
538    // SAFETY:
539    // Since this function is only called on the main thread, the stack will
540    // not be reused until program exit, so the runtime will never observe
541    // that part of the stack has been made unusable in this way.
542    let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) };
543    if result != 0 {
544        {
    ::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());
545    }
546
547    let guardaddr = stackptr.addr();
548
549    Some(guardaddr..guardaddr + page_size)
550}
551
552#[cfg(any(
553    target_os = "macos",
554    target_os = "openbsd",
555    target_os = "solaris",
556    target_os = "illumos",
557))]
558fn current_guard() -> Option<Range<usize>> {
559    let stackptr = get_stack_start()?;
560    let stackaddr = stackptr.addr();
561    Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
562}
563
564#[cfg(any(
565    target_os = "android",
566    target_os = "freebsd",
567    target_os = "hurd",
568    target_os = "linux",
569    target_os = "netbsd",
570    target_os = "l4re"
571))]
572fn 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    // SAFETY:
587    // The attributes object has not been initialized yet and will not be
588    // moved until destroyed.
589    #[cfg(target_os = "freebsd")]
590    assert_eq!(unsafe { libc::pthread_attr_init(attr.get()) }, 0);
591    // SAFETY:
592    // * calling `pthread_self` is always valid and returns a valid `pthread_t`
593    // * `attr` is an initialized attribute object that can be written to.
594    #[cfg(target_os = "freebsd")]
595    let e = unsafe { libc::pthread_attr_get_np(libc::pthread_self(), attr.get()) };
596    // SAFETY:
597    // * calling `pthread_self` is always valid and returns a valid `pthread_t`
598    // * `attr` can be written to, and will be initialized by this call.
599    #[cfg(not(target_os = "freebsd"))]
600    let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) };
601    if e == 0 {
602        let mut guardsize = 0;
603        // SAFETY:
604        // `attr` is an initialized attribute object and the pointer is valid
605        // for writing.
606        {
    match (&unsafe {
                    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!(unsafe { libc::pthread_attr_getguardsize(attr.get(), &mut guardsize) }, 0);
607        if guardsize == 0 {
608            if falsecfg!(all(target_os = "linux", target_env = "musl")) {
609                // musl versions before 1.1.19 always reported guard
610                // size obtained from pthread_attr_get_np as zero.
611                // Use page size as a fallback.
612                guardsize = PAGE_SIZE.load(Ordering::Relaxed);
613            } else {
614                { ::core::panicking::panic_fmt(format_args!("there is no guard page")); };panic!("there is no guard page");
615            }
616        }
617        let mut stackptr = crate::ptr::null_mut::<libc::c_void>();
618        let mut size = 0;
619        // SAFETY:
620        // `attr` is an initialized attribute object and both the pointers
621        // are valid for writing.
622        {
    match (&unsafe {
                    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!(unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackptr, &mut size) }, 0);
623
624        let stackaddr = stackptr.addr();
625        ret = if falsecfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) {
626            Some(stackaddr - guardsize..stackaddr)
627        } else if falsecfg!(all(target_os = "linux", target_env = "musl")) {
628            Some(stackaddr - guardsize..stackaddr)
629        } else if truecfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) {
630            // glibc used to include the guard area within the stack, as noted in the BUGS
631            // section of `man pthread_attr_getguardsize`. This has been corrected starting
632            // with glibc 2.27, and in some distro backports, so the guard is now placed at the
633            // end (below) the stack. There's no easy way for us to know which we have at
634            // runtime, so we'll just match any fault in the range right above or below the
635            // stack base to call that fault a stack overflow.
636            Some(stackaddr - guardsize..stackaddr + guardsize)
637        } else {
638            Some(stackaddr..stackaddr + guardsize)
639        };
640    }
641    if e == 0 || falsecfg!(target_os = "freebsd") {
642        // SAFETY:
643        // `attr` was initialized either by `pthread_attr_init` (FreeBSD) or
644        // by `pthread_attr_get_np`, and is not used after this point.
645        {
    match (&unsafe { 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!(unsafe { libc::pthread_attr_destroy(attr.get()) }, 0);
646    }
647    ret
648}