1#![cfg_attr(test, allow(dead_code))]
23pub use self::imp::init;
4use self::imp::{drop_handler, make_handler};
56pub struct Handler {
7 data: *mut libc::c_void,
8}
910impl Handler {
11pub unsafe fn new() -> Handler {
12make_handler(false)
13 }
1415fn null() -> Handler {
16Handler { data: crate::ptr::null_mut() }
17 }
18}
1920impl Dropfor Handler {
21fn drop(&mut self) {
22unsafe {
23drop_handler(self.data);
24 }
25 }
26}
2728#[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;
4243// 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 {
60use libc::{
61MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK,
62SA_SIGINFO, SIG_DFL, SIGBUS, SIGSEGV, SS_DISABLE, sigaction, sigaltstack, sighandler_t,
63 };
64#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
65use libc::{mmap as mmap64, mprotect, munmap};
66#[cfg(all(target_os = "linux", target_env = "gnu"))]
67use libc::{mmap64, mprotect, munmap};
6869use super::Handler;
70use super::thread_info::{delete_current_info, set_current_info, with_current_info};
71use crate::ops::Range;
72use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
73use crate::sys::pal::unix::conf;
74use crate::{io, mem, ptr};
7576// 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)]
101unsafe 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`.
107let fault_addr = unsafe { (*info).si_addr().addr() };
108109// `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.
113if fault_addr != 0 {
114with_current_info(|thread_info| {
115// If the faulting address is within the guard page, then we print a
116 // message saying so and abort.
117if let Some(thread_info) = thread_info118 && 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.
127let tid = thread_info.tid;
128let name = thread_info.name.as_deref().unwrap_or("<unknown>");
129if 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 }
134135// Unregister ourselves by reverting back to the default behavior.
136 // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
137let mut action: sigaction = unsafe { mem::zeroed() };
138action.sa_sigaction = SIG_DFL;
139// SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
140unsafe { sigaction(signum, &action, ptr::null_mut()) };
141142// See comment above for why this function returns.
143}
144145static 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).
152static MAIN_ALTSTACK: Atomic<*mut libc::c_void> = AtomicPtr::new(ptr::null_mut());
153static NEED_ALTSTACK: Atomic<bool> = AtomicBool::new(false);
154155/// # Safety
156 /// Must be called only once
157#[forbid(unsafe_op_in_unsafe_fn)]
158pub unsafe fn init() {
159PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed);
160161let mut guard_page_range = unsafe { install_main_guard() };
162163// 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.
166if falsecfg!(panic = "immediate-abort") {
167return;
168 }
169170// SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
171let mut action: sigaction = unsafe { mem::zeroed() };
172for &signal in &[SIGSEGV, SIGBUS] {
173// SAFETY: just fetches the current signal handler into action
174unsafe { sigaction(signal, ptr::null_mut(), &mut action) };
175// Configure our signal handler if one is not already set.
176if action.sa_sigaction == SIG_DFL {
177if !NEED_ALTSTACK.load(Ordering::Relaxed) {
178// haven't set up our sigaltstack yet
179NEED_ALTSTACK.store(true, Ordering::Release);
180let handler = unsafe { make_handler(true) };
181 MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
182 mem::forget(handler);
183184if let Some(guard_page_range) = guard_page_range.take() {
185 set_current_info(guard_page_range);
186 }
187 }
188189 action.sa_flags = SA_SIGINFO | SA_ONSTACK;
190 action.sa_sigaction = signal_handler
191as unsafe extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)
192as sighandler_t;
193// SAFETY: only overriding signals if the default is set
194unsafe { sigaction(signal, &action, ptr::null_mut()) };
195 }
196 }
197 }
198199unsafe 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 ))]
209let 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 )))]
216let flags = MAP_PRIVATE | MAP_ANON;
217218let sigstack_size = sigstack_size();
219let page_size = PAGE_SIZE.load(Ordering::Relaxed);
220221let stackp = mmap64(
222 ptr::null_mut(),
223sigstack_size + page_size,
224PROT_READ | PROT_WRITE,
225flags,
226 -1,
2270,
228 );
229if 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 }
232let guard_result = libc::mprotect(stackp, page_size, PROT_NONE);
233if 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 }
236let stackp = stackp.add(page_size);
237238 libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size }
239 }
240241/// # Safety
242 /// Mutates the alternate signal stack
243#[forbid(unsafe_op_in_unsafe_fn)]
244pub unsafe fn make_handler(main_thread: bool) -> Handler {
245if falsecfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) {
246return Handler::null();
247 }
248249if !main_thread {
250if let Some(guard_page_range) = unsafe { current_guard() } {
251set_current_info(guard_page_range);
252 }
253 }
254255// SAFETY: assuming stack_t is zero-initializable
256let mut stack = unsafe { mem::zeroed() };
257// SAFETY: reads current stack_t into stack
258unsafe { sigaltstack(ptr::null(), &mut stack) };
259// Configure alternate signal stack, if one is not already set.
260if stack.ss_flags & SS_DISABLE != 0 {
261// SAFETY: We warned our caller this would happen!
262unsafe {
263stack = get_stack();
264sigaltstack(&stack, ptr::null_mut());
265 }
266Handler { data: stack.ss_sp as *mut libc::c_void }
267 } else {
268Handler::null()
269 }
270 }
271272/// # 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)]
278pub unsafe fn drop_handler(data: *mut libc::c_void) {
279if !data.is_null() {
280let sigstack_size = sigstack_size();
281let page_size = PAGE_SIZE.load(Ordering::Relaxed);
282let 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.
289ss_size: sigstack_size,
290 };
291// SAFETY: we warned the caller this disables the alternate signal stack!
292unsafe { 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.
295unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };
296 }
297298delete_current_info();
299 }
300301/// 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")))]
303fn sigstack_size() -> usize {
304let 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
308libc::SIGSTKSZ.max(dynamic_sigstkszas _)
309 }
310311/// Not all OS support hardware where this is needed.
312#[cfg(not(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc"))))]
313fn sigstack_size() -> usize {
314 libc::SIGSTKSZ
315 }
316317#[cfg(any(target_os = "solaris", target_os = "illumos"))]
318unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
319let mut current_stack: libc::stack_t = crate::mem::zeroed();
320assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
321Some(current_stack.ss_sp)
322 }
323324#[cfg(target_os = "macos")]
325unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
326let th = libc::pthread_self();
327let stackptr = libc::pthread_get_stackaddr_np(th);
328Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th)))
329 }
330331#[cfg(target_os = "openbsd")]
332unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
333let mut current_stack: libc::stack_t = crate::mem::zeroed();
334assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
335336let stack_ptr = current_stack.ss_sp;
337let stackaddr = if libc::pthread_main_np() == 1 {
338// main thread
339stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
340 } else {
341// new thread
342stack_ptr.addr() - current_stack.ss_size
343 };
344Some(stack_ptr.with_addr(stackaddr))
345 }
346347#[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))]
355unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
356use crate::pin::pin;
357use crate::sys::helpers::COpaque;
358359let mut ret = None;
360let mut attr: COpaque<libc::pthread_attr_t> = COpaque::uninit();
361if !falsecfg!(target_os = "freebsd") {
362attr = COpaque::zeroed();
363 }
364let 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.
366let attr = attr.into_ref();
367368#[cfg(target_os = "freebsd")]
369assert_eq!(libc::pthread_attr_init(attr.get()), 0);
370#[cfg(target_os = "freebsd")]
371let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.get());
372#[cfg(not(target_os = "freebsd"))]
373let e = libc::pthread_getattr_np(libc::pthread_self(), attr.get());
374if e == 0 {
375let mut stackaddr = crate::ptr::null_mut();
376let 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);
378ret = Some(stackaddr);
379 }
380if 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 }
383ret384 }
385386fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> {
387let stackptr = unsafe { get_stack_start()? };
388let stackaddr = stackptr.addr();
389390// 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
396let remainder = stackaddr % page_size;
397Some(if remainder == 0 {
398stackptr399 } else {
400stackptr.with_addr(stackaddr + page_size - remainder)
401 })
402 }
403404#[forbid(unsafe_op_in_unsafe_fn)]
405unsafe fn install_main_guard() -> Option<Range<usize>> {
406let page_size = PAGE_SIZE.load(Ordering::Relaxed);
407408unsafe {
409// this way someone on any unix-y OS can check that all these compile
410if truecfg!(all(target_os = "linux", not(target_env = "musl"))) {
411install_main_guard_linux(page_size)
412 } else if falsecfg!(all(target_os = "linux", target_env = "musl")) {
413install_main_guard_linux_musl(page_size)
414 } else if falsecfg!(target_os = "freebsd") {
415#[cfg(not(target_os = "freebsd"))]
416return None;
417// The FreeBSD code cannot be checked on non-BSDs.
418#[cfg(target_os = "freebsd")]
419install_main_guard_freebsd(page_size)
420 } else if falsecfg!(any(target_os = "netbsd", target_os = "openbsd")) {
421install_main_guard_bsds(page_size)
422 } else {
423install_main_guard_default(page_size)
424 }
425 }
426 }
427428#[forbid(unsafe_op_in_unsafe_fn)]
429unsafe 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
432if falsecfg!(panic = "immediate-abort") {
433return 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.
445let stackptr = stack_start_aligned(page_size)?;
446let stackaddr = stackptr.addr();
447Some(stackaddr - page_size..stackaddr)
448 }
449450#[forbid(unsafe_op_in_unsafe_fn)]
451unsafe 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.
456None457 }
458459#[forbid(unsafe_op_in_unsafe_fn)]
460 #[cfg(target_os = "freebsd")]
461unsafe fn install_main_guard_freebsd(page_size: usize) -> Option<Range<usize>> {
462// See the corresponding conditional in install_main_guard_linux().
463if cfg!(panic = "immediate-abort") {
464return 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.
470let stackptr = stack_start_aligned(page_size)?;
471let 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.
476static PAGES: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new();
477478let pages = PAGES.get_or_init(|| {
479let mut guard: usize = 0;
480let mut size = size_of_val(&guard);
481let oid = c"security.bsd.stack_guard_page";
482483let r = unsafe {
484 libc::sysctlbyname(
485 oid.as_ptr(),
486 (&raw mut guard).cast(),
487&raw mut size,
488 ptr::null_mut(),
4890,
490 )
491 };
492if r == 0 { guard } else { 1 }
493 });
494Some(guardaddr..guardaddr + pages * page_size)
495 }
496497#[forbid(unsafe_op_in_unsafe_fn)]
498unsafe fn install_main_guard_bsds(page_size: usize) -> Option<Range<usize>> {
499// See the corresponding conditional in install_main_guard_linux().
500if falsecfg!(panic = "immediate-abort") {
501return 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.
510let stackptr = stack_start_aligned(page_size)?;
511let stackaddr = stackptr.addr();
512Some(stackaddr - page_size..stackaddr)
513 }
514515#[forbid(unsafe_op_in_unsafe_fn)]
516unsafe 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.
525let stackptr = stack_start_aligned(page_size)?;
526let result = unsafe {
527mmap64(
528stackptr,
529page_size,
530PROT_READ | PROT_WRITE,
531MAP_PRIVATE | MAP_ANON | MAP_FIXED,
532 -1,
5330,
534 )
535 };
536if 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 }
539540let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) };
541if 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 }
544545let guardaddr = stackptr.addr();
546547Some(guardaddr..guardaddr + page_size)
548 }
549550#[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.
557unsafe fn current_guard() -> Option<Range<usize>> {
558let stackptr = get_stack_start()?;
559let stackaddr = stackptr.addr();
560Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
561 }
562563#[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.
572unsafe fn current_guard() -> Option<Range<usize>> {
573use crate::pin::pin;
574use crate::sys::helpers::COpaque;
575576let mut ret = None;
577578let mut attr: COpaque<libc::pthread_attr_t> = COpaque::uninit();
579if !falsecfg!(target_os = "freebsd") {
580attr = COpaque::zeroed();
581 }
582let 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.
584let attr = attr.into_ref();
585586#[cfg(target_os = "freebsd")]
587assert_eq!(libc::pthread_attr_init(attr.get()), 0);
588#[cfg(target_os = "freebsd")]
589let e = libc::pthread_attr_get_np(libc::pthread_self(), attr.get());
590#[cfg(not(target_os = "freebsd"))]
591let e = libc::pthread_getattr_np(libc::pthread_self(), attr.get());
592if e == 0 {
593let 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);
595if guardsize == 0 {
596if 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.
600guardsize = 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 }
605let mut stackptr = crate::ptr::null_mut::<libc::c_void>();
606let 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);
608609let stackaddr = stackptr.addr();
610ret = if falsecfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) {
611Some(stackaddr - guardsize..stackaddr)
612 } else if falsecfg!(all(target_os = "linux", target_env = "musl")) {
613Some(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.
622Some(stackaddr - guardsize..stackaddr + guardsize)
623 } else {
624Some(stackaddr..stackaddr + guardsize)
625 };
626 }
627if 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 }
630ret631 }
632}
633634// 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 {
657pub unsafe fn init() {}
658659pub unsafe fn make_handler(_main_thread: bool) -> super::Handler {
660super::Handler::null()
661 }
662663pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
664}
665666#[cfg(target_os = "cygwin")]
667mod imp {
668mod c {
669pub type PVECTORED_EXCEPTION_HANDLER =
670Option<unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32>;
671pub type NTSTATUS = i32;
672pub type BOOL = i32;
673674unsafe extern "system" {
675pub fn AddVectoredExceptionHandler(
676 first: u32,
677 handler: PVECTORED_EXCEPTION_HANDLER,
678 ) -> *mut core::ffi::c_void;
679pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL;
680 }
681682pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _;
683pub const EXCEPTION_CONTINUE_SEARCH: i32 = 1i32;
684685#[repr(C)]
686 #[derive(Clone, Copy)]
687pub struct EXCEPTION_POINTERS {
688pub 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)]
694pub struct EXCEPTION_RECORD {
695pub ExceptionCode: NTSTATUS,
696pub ExceptionFlags: u32,
697pub ExceptionRecord: *mut EXCEPTION_RECORD,
698pub ExceptionAddress: *mut core::ffi::c_void,
699pub NumberParameters: u32,
700pub ExceptionInformation: [usize; 15],
701 }
702 }
703704/// Reserve stack space for use in stack overflow exceptions.
705fn reserve_stack() {
706let 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.
709debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling");
710 }
711712unsafe 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.
714unsafe {
715let rec = &(*(*ExceptionInfo).ExceptionRecord);
716let code = rec.ExceptionCode;
717718if code == c::EXCEPTION_STACK_OVERFLOW {
719crate::thread::with_current_name(|name| {
720let name = name.unwrap_or("<unknown>");
721let tid = crate::thread::current_os_id();
722rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n");
723 });
724 }
725 c::EXCEPTION_CONTINUE_SEARCH
726 }
727 }
728729pub unsafe fn init() {
730// SAFETY: `vectored_handler` has the correct ABI and is safe to call during exception handling.
731unsafe {
732let 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.
735debug_assert!(!result.is_null(), "failed to install exception handler");
736 }
737// Set the thread stack guarantee for the main thread.
738reserve_stack();
739 }
740741pub unsafe fn make_handler(main_thread: bool) -> super::Handler {
742if !main_thread {
743 reserve_stack();
744 }
745super::Handler::null()
746 }
747748pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
749}