Skip to content

Add stack overflow handler for cygwin #138609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 22, 2025
Merged
Changes from all 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
87 changes: 87 additions & 0 deletions library/std/src/sys/pal/unix/stack_overflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ mod imp {
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos",
target_os = "cygwin",
)))]
mod imp {
pub unsafe fn init() {}
Expand All @@ -597,3 +598,89 @@ mod imp {

pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
}

#[cfg(target_os = "cygwin")]
mod imp {
mod c {
pub type PVECTORED_EXCEPTION_HANDLER =
Option<unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32>;
pub type NTSTATUS = i32;
pub type BOOL = i32;

unsafe extern "system" {
pub fn AddVectoredExceptionHandler(
first: u32,
handler: PVECTORED_EXCEPTION_HANDLER,
) -> *mut core::ffi::c_void;
pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL;
}

pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _;
pub const EXCEPTION_CONTINUE_SEARCH: i32 = 1i32;

#[repr(C)]
#[derive(Clone, Copy)]
pub struct EXCEPTION_POINTERS {
pub ExceptionRecord: *mut EXCEPTION_RECORD,
// We don't need this field here
// pub Context: *mut CONTEXT,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EXCEPTION_RECORD {
pub ExceptionCode: NTSTATUS,
pub ExceptionFlags: u32,
pub ExceptionRecord: *mut EXCEPTION_RECORD,
pub ExceptionAddress: *mut core::ffi::c_void,
pub NumberParameters: u32,
pub ExceptionInformation: [usize; 15],
}
}

/// Reserve stack space for use in stack overflow exceptions.
fn reserve_stack() {
let result = unsafe { c::SetThreadStackGuarantee(&mut 0x5000) };
// Reserving stack space is not critical so we allow it to fail in the released build of libstd.
// We still use debug assert here so that CI will test that we haven't made a mistake calling the function.
debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling");
}

unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> i32 {
// SAFETY: It's up to the caller (which in this case is the OS) to ensure that `ExceptionInfo` is valid.
unsafe {
let rec = &(*(*ExceptionInfo).ExceptionRecord);
let code = rec.ExceptionCode;

if code == c::EXCEPTION_STACK_OVERFLOW {
crate::thread::with_current_name(|name| {
let name = name.unwrap_or("<unknown>");
rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
});
}
c::EXCEPTION_CONTINUE_SEARCH
}
}

pub unsafe fn init() {
// SAFETY: `vectored_handler` has the correct ABI and is safe to call during exception handling.
unsafe {
let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler));
// Similar to the above, adding the stack overflow handler is allowed to fail
// but a debug assert is used so CI will still test that it normally works.
debug_assert!(!result.is_null(), "failed to install exception handler");
}
// Set the thread stack guarantee for the main thread.
reserve_stack();
}

pub unsafe fn cleanup() {}

pub unsafe fn make_handler(main_thread: bool) -> super::Handler {
if !main_thread {
reserve_stack();
}
super::Handler::null()
}

pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
}
Loading