Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions library/std/src/sys/unix/stack_overflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ mod imp {
use crate::io;
use crate::mem;
use crate::ptr;
use crate::thread;
use crate::sys_common::thread_info::current_thread;

use libc::MAP_FAILED;
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
Expand Down Expand Up @@ -89,10 +89,9 @@ mod imp {
// If the faulting address is within the guard page, then we print a
// message saying so and abort.
if guard.start <= addr && addr < guard.end {
rtprintpanic!(
"\nthread '{}' has overflowed its stack\n",
thread::current().name().unwrap_or("<unknown>")
);
let thread = current_thread();
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unknown>");
rtprintpanic!("\nthread '{}' has overflowed its stack\n", name);
rtabort!("stack overflow");
} else {
// Unregister ourselves by reverting back to the default behavior.
Expand Down
58 changes: 26 additions & 32 deletions library/std/src/sys_common/thread_info.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,41 @@
#![allow(dead_code)] // stack_guard isn't used right now on all platforms
#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny

use crate::cell::RefCell;
use crate::cell::Cell;
use crate::sys::thread::guard::Guard;
use crate::thread::Thread;

struct ThreadInfo {
stack_guard: Option<Guard>,
thread: Thread,
}

thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = const { RefCell::new(None) } }

impl ThreadInfo {
fn with<R, F>(f: F) -> Option<R>
where
F: FnOnce(&mut ThreadInfo) -> R,
{
THREAD_INFO
.try_with(move |thread_info| {
let mut thread_info = thread_info.borrow_mut();
let thread_info = thread_info.get_or_insert_with(|| ThreadInfo {
stack_guard: None,
thread: Thread::new(None),
});
f(thread_info)
})
.ok()
}
thread_local! {
static THREAD: Cell<Option<Thread>> = const { Cell::new(None) };
// Use a separate thread local for the stack guard page location.
// Since `Guard` does not implement drop, this is always available
// on systems with ELF-TLS, in particular during TLS destruction.
static STACK_GUARD: Cell<Option<Guard>> = const { Cell::new(None) };
}

pub fn current_thread() -> Option<Thread> {
ThreadInfo::with(|info| info.thread.clone())
THREAD
.try_with(|thread| {
let t = thread.take().unwrap_or_else(|| Thread::new(None));
let t2 = t.clone();
thread.set(Some(t));
t2
})
.ok()
}

pub fn stack_guard() -> Option<Guard> {
ThreadInfo::with(|info| info.stack_guard.clone()).and_then(|o| o)
STACK_GUARD
.try_with(|guard| {
let g = guard.take();
let g2 = g.clone();
guard.set(g);
g2
})
.ok()
.flatten()
}

pub fn set(stack_guard: Option<Guard>, thread: Thread) {
THREAD_INFO.with(move |thread_info| {
let mut thread_info = thread_info.borrow_mut();
rtassert!(thread_info.is_none());
*thread_info = Some(ThreadInfo { stack_guard, thread });
});
rtassert!(STACK_GUARD.replace(stack_guard).is_none());
rtassert!(THREAD.replace(Some(thread)).is_none());
}
21 changes: 21 additions & 0 deletions tests/ui/abi/stack-probes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ fn main() {
if args.len() > 0 {
match &args[0][..] {
"main-recurse" => overflow_recurse(),
"main-tls-recurse" => tls_recurse(),
"child-recurse" => thread::spawn(overflow_recurse).join().unwrap(),
"child-tls-recurse" => thread::spawn(tls_recurse).join().unwrap(),
"child-frame" => overflow_frame(),
_ => panic!(),
}
Expand All @@ -42,8 +44,13 @@ fn main() {
// details
if cfg!(not(target_os = "linux")) {
assert_overflow(Command::new(&me).arg("main-recurse"));
// FIXME: This does not seem to work on macOS.
if cfg!(not(target_os = "macos")) {
assert_overflow(Command::new(&me).arg("main-tls-recurse"));
}
}
assert_overflow(Command::new(&me).arg("child-recurse"));
assert_overflow(Command::new(&me).arg("child-tls-recurse"));
assert_overflow(Command::new(&me).arg("child-frame"));
}

Expand All @@ -56,6 +63,20 @@ fn recurse(array: &MaybeUninit<[u64; 1024]>) {
recurse(&local);
}

fn tls_recurse() {
struct RecursiveDrop;

impl Drop for RecursiveDrop {
fn drop(&mut self) {
overflow_recurse();
}
}

thread_local!(static LOCAL: RecursiveDrop = const { RecursiveDrop });

LOCAL.with(|_| {});
}

#[inline(never)]
fn overflow_recurse() {
recurse(&MaybeUninit::uninit());
Expand Down