1use libc::{
2MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK,
3SA_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};
910use 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};
1617/// 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`.
50let fault_addr = unsafe { (*info).si_addr().addr() };
5152// `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.
56if fault_addr != 0 {
57with_current_info(|thread_info| {
58// If the faulting address is within the guard page, then we print a
59 // message saying so and abort.
60if let Some(thread_info) = thread_info61 && 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.
70let tid = thread_info.tid;
71let name = thread_info.name.as_deref().unwrap_or("<unknown>");
72if 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 }
7778// Unregister ourselves by reverting back to the default behavior.
79 // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
80let mut action: sigaction = unsafe { mem::zeroed() };
81action.sa_sigaction = SIG_DFL;
82// SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
83unsafe { sigaction(signum, &action, ptr::null_mut()) };
8485// See comment above for why this function returns.
86}
8788static 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);
9798/// # Safety
99/// Must be called only once, on the main thread, during program startup.
100pub unsafe fn init() {
101PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed);
102103// 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.
107let mut guard_page_range = unsafe { install_main_guard() };
108109// 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.
112if falsecfg!(panic = "immediate-abort") {
113return;
114 }
115116// SAFETY: C structures are always zero-initializable.
117let mut action: sigaction = unsafe { mem::zeroed() };
118for &signal in &[SIGSEGV, SIGBUS] {
119// SAFETY: just fetches the current signal handler into action
120unsafe { 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.
125if action.sa_sigaction == SIG_DFL {
126if !NEED_ALTSTACK.load(Ordering::Relaxed) {
127// haven't set up our sigaltstack yet
128NEED_ALTSTACK.store(true, Ordering::Release);
129let handler = make_handler(true);
130 MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
131 mem::forget(handler);
132133if let Some(guard_page_range) = guard_page_range.take() {
134 set_current_info(guard_page_range);
135 }
136 }
137138 action.sa_flags = SA_SIGINFO | SA_ONSTACK;
139 action.sa_sigaction = signal_handler
140as unsafe extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)
141as 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`.
145unsafe { sigaction(signal, &action, ptr::null_mut()) };
146 }
147 }
148}
149150fn 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 ))]
160let 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 )))]
167let flags = MAP_PRIVATE | MAP_ANON;
168169let sigstack_size = sigstack_size();
170let page_size = PAGE_SIZE.load(Ordering::Relaxed);
171172// SAFETY: this does not unmap any existing pages.
173let stackp = unsafe {
174mmap64(ptr::null_mut(), sigstack_size + page_size, PROT_READ | PROT_WRITE, flags, -1, 0)
175 };
176if 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.
180let guard_result = unsafe { libc::mprotect(stackp, page_size, PROT_NONE) };
181if 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.
187let stackp = unsafe { stackp.add(page_size) };
188189 libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size }
190}
191192pub fn make_handler(main_thread: bool) -> Handler {
193if falsecfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) {
194return Handler::null();
195 }
196197if !main_thread {
198if let Some(guard_page_range) = current_guard() {
199set_current_info(guard_page_range);
200 }
201 }
202203// 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.
207let mut stack = unsafe { mem::zeroed() };
208// SAFETY: `&mut stack` is valid for writing a `stack_t`.
209unsafe { sigaltstack(ptr::null(), &mut stack) };
210211// Configure alternate signal stack, if one is not already set.
212if stack.ss_flags & SS_DISABLE != 0 {
213let 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.
227unsafe { sigaltstack(&stack, ptr::null_mut()) };
228Handler { data: stack.ss_sp as *mut libc::c_void }
229 } else {
230Handler::null()
231 }
232}
233234/// # 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) {
238if !data.is_null() {
239let sigstack_size = sigstack_size();
240let page_size = PAGE_SIZE.load(Ordering::Relaxed);
241let 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.
248ss_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.
258unsafe { 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.
266unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };
267 }
268269delete_current_info();
270}
271272/// 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.
276let 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
280libc::SIGSTKSZ.max(dynamic_sigstkszas _)
281}
282283/// 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}
288289#[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.
292let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() };
293// SAFETY:
294 // The pointer is valid for writing a `stack_t`.
295assert_eq!(unsafe { libc::stack_getbounds(&mut current_stack) }, 0);
296Some(current_stack.ss_sp)
297}
298299#[cfg(target_os = "macos")]
300fn get_stack_start() -> Option<*mut libc::c_void> {
301// SAFETY: always safe to call.
302let th = unsafe { libc::pthread_self() };
303// SAFETY: `th` is a valid `pthread_t`.
304unsafe {
305let stackptr = libc::pthread_get_stackaddr_np(th);
306let stacksize = libc::pthread_get_stacksize_np(th);
307Some(stackptr.map_addr(|addr| addr - stacksize))
308 }
309}
310311#[cfg(target_os = "openbsd")]
312fn get_stack_start() -> Option<*mut libc::c_void> {
313// SAFETY: C types are always zero-initializable.
314let 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`.
319assert_eq!(unsafe { libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack) }, 0);
320321let stack_ptr = current_stack.ss_sp;
322// SAFETY: this is always safe to call.
323let stackaddr = if unsafe { libc::pthread_main_np() } == 1 {
324// main thread
325stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
326 } else {
327// new thread
328stack_ptr.addr() - current_stack.ss_size
329 };
330Some(stack_ptr.with_addr(stackaddr))
331}
332333#[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> {
342use crate::pin::pin;
343use crate::sys::helpers::COpaque;
344345let mut ret = None;
346let mut attr: COpaque<libc::pthread_attr_t> = COpaque::uninit();
347if !falsecfg!(target_os = "freebsd") {
348attr = COpaque::zeroed();
349 }
350let 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.
352let attr = attr.into_ref();
353354// SAFETY:
355 // The attributes object has not been initialized yet and will not be
356 // moved until destroyed.
357#[cfg(target_os = "freebsd")]
358assert_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")]
363let 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"))]
368let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) };
369if e == 0 {
370let mut stackaddr = crate::ptr::null_mut();
371let 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!(
376unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackaddr, &mut stacksize) },
3770
378);
379ret = Some(stackaddr);
380 }
381if 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 }
387ret388}
389390fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> {
391let stackptr = get_stack_start()?;
392let stackaddr = stackptr.addr();
393394// 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
400let remainder = stackaddr % page_size;
401Some(if remainder == 0 {
402stackptr403 } else {
404stackptr.with_addr(stackaddr + page_size - remainder)
405 })
406}
407408/// # 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>> {
412let page_size = PAGE_SIZE.load(Ordering::Relaxed);
413414// this way someone on any unix-y OS can check that all these compile
415if truecfg!(all(target_os = "linux", not(target_env = "musl"))) {
416install_main_guard_linux(page_size)
417 } else if falsecfg!(all(target_os = "linux", target_env = "musl")) {
418install_main_guard_linux_musl(page_size)
419 } else if falsecfg!(target_os = "freebsd") {
420#[cfg(not(target_os = "freebsd"))]
421return None;
422// The FreeBSD code cannot be checked on non-BSDs.
423#[cfg(target_os = "freebsd")]
424install_main_guard_freebsd(page_size)
425 } else if falsecfg!(any(target_os = "netbsd", target_os = "openbsd")) {
426install_main_guard_bsds(page_size)
427 } else {
428// SAFETY: guaranteed by caller.
429unsafe { install_main_guard_default(page_size) }
430 }
431}
432433fn 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
436if falsecfg!(panic = "immediate-abort") {
437return 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.
449let stackptr = stack_start_aligned(page_size)?;
450let stackaddr = stackptr.addr();
451Some(stackaddr - page_size..stackaddr)
452}
453454fn 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.
459None460}
461462#[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().
465if cfg!(panic = "immediate-abort") {
466return 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.
472let stackptr = stack_start_aligned(page_size)?;
473let guardaddr = stackptr.addr();
474475// 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.
478let mut guard: usize = 0;
479let mut size = size_of_val(&guard);
480let oid = c"security.bsd.stack_guard_page";
481let r = unsafe {
482 libc::sysctlbyname(oid.as_ptr(), (&raw mut guard).cast(), &raw mut size, ptr::null_mut(), 0)
483 };
484let pages = if r == 0 { guard } else { 1 };
485486Some(guardaddr..guardaddr + pages * page_size)
487}
488489fn install_main_guard_bsds(page_size: usize) -> Option<Range<usize>> {
490// See the corresponding conditional in install_main_guard_linux().
491if falsecfg!(panic = "immediate-abort") {
492return 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.
501let stackptr = stack_start_aligned(page_size)?;
502let stackaddr = stackptr.addr();
503Some(stackaddr - page_size..stackaddr)
504}
505506/// # 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.
518let 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.
524let result = unsafe {
525mmap64(
526stackptr,
527page_size,
528PROT_READ | PROT_WRITE,
529MAP_PRIVATE | MAP_ANON | MAP_FIXED,
530 -1,
5310,
532 )
533 };
534if 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 }
537538// 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.
542let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) };
543if 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 }
546547let guardaddr = stackptr.addr();
548549Some(guardaddr..guardaddr + page_size)
550}
551552#[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>> {
559let stackptr = get_stack_start()?;
560let stackaddr = stackptr.addr();
561Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
562}
563564#[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>> {
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// SAFETY:
587 // The attributes object has not been initialized yet and will not be
588 // moved until destroyed.
589#[cfg(target_os = "freebsd")]
590assert_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")]
595let 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"))]
600let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) };
601if e == 0 {
602let 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);
607if guardsize == 0 {
608if 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.
612guardsize = 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 }
617let mut stackptr = crate::ptr::null_mut::<libc::c_void>();
618let 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);
623624let stackaddr = stackptr.addr();
625ret = if falsecfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) {
626Some(stackaddr - guardsize..stackaddr)
627 } else if falsecfg!(all(target_os = "linux", target_env = "musl")) {
628Some(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.
636Some(stackaddr - guardsize..stackaddr + guardsize)
637 } else {
638Some(stackaddr..stackaddr + guardsize)
639 };
640 }
641if 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 }
647ret648}