about summary refs log tree commit diff
path: root/library/std/src/sys/pal/unix/stack_overflow.rs
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-03-22 20:52:30 +0000
committerbors <bors@rust-lang.org>2025-03-22 20:52:30 +0000
commitb48576b4db5a595f453891f0b7243ef75d8c0afa (patch)
treee9d3a1a7b8b1d4182a343702d45abc844ce7b846 /library/std/src/sys/pal/unix/stack_overflow.rs
parentd93f678fa55842cccd2f091deccd93e9494b3764 (diff)
parent2644500bff498ddbb2d1237c4ee43af2ef60a084 (diff)
downloadrust-b48576b4db5a595f453891f0b7243ef75d8c0afa.tar.gz
rust-b48576b4db5a595f453891f0b7243ef75d8c0afa.zip
Auto merge of #138831 - matthiaskrgr:rollup-3t0dqiz, r=matthiaskrgr
Rollup of 7 pull requests

Successful merges:

 - #138609 (Add stack overflow handler for cygwin)
 - #138639 (Clean UI tests 2 of n)
 - #138773 (catch_unwind intrinsic: document return value)
 - #138782 (test(ui): add tuple-struct-where-clause-suggestion ui test for #91520)
 - #138794 (expand: Do not report `cfg_attr` traces on macros as unused attributes)
 - #138801 (triagebot: add autolabel rules for D-* and L-*)
 - #138804 (Allow inlining for `Atomic*::from_ptr`)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library/std/src/sys/pal/unix/stack_overflow.rs')
-rw-r--r--library/std/src/sys/pal/unix/stack_overflow.rs87
1 files changed, 87 insertions, 0 deletions
diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs
index 0ecccdc8812..4bd0cedd44c 100644
--- a/library/std/src/sys/pal/unix/stack_overflow.rs
+++ b/library/std/src/sys/pal/unix/stack_overflow.rs
@@ -585,6 +585,7 @@ mod imp {
     target_os = "openbsd",
     target_os = "solaris",
     target_os = "illumos",
+    target_os = "cygwin",
 )))]
 mod imp {
     pub unsafe fn init() {}
@@ -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) {}
+}