From 9e182c3c178dc2ebe16d284d8d124f7141f8be05 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 18 Feb 2025 13:18:32 +0100 Subject: [PATCH 01/16] improve error handling see https://github.com/rust-lang/stacker/issues/115 See https://github.com/sqlpage/SQLPage/issues/814 --- src/lib.rs | 60 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9f61d7f..de0825e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -163,10 +163,7 @@ psm_stack_manipulation! { -1, // Some implementations assert fd = -1 if MAP_ANON is specified 0 ); - if new_stack == libc::MAP_FAILED { - let error = std::io::Error::last_os_error(); - panic!("allocating stack failed with: {}", error) - } + assert_ne!(new_stack, libc::MAP_FAILED, "mmap failed to allocate stack: {}", std::io::Error::last_os_error()); let guard = StackRestoreGuard { new_stack, stack_bytes, @@ -191,11 +188,7 @@ psm_stack_manipulation! { } else { -1 }; - if result == -1 { - let error = std::io::Error::last_os_error(); - drop(guard); - panic!("setting stack permissions failed with: {}", error) - } + assert_ne!(result, -1, "mprotect/mmap failed: {}", std::io::Error::last_os_error()); guard } } @@ -222,7 +215,7 @@ psm_stack_manipulation! { fn _grow(stack_size: usize, callback: &mut dyn FnMut()) { // Calculate a number of pages we want to allocate for the new stack. // For maximum portability we want to produce a stack that is aligned to a page and has - // a size that’s a multiple of page size. Furthermore we want to allocate two extras pages + // a size that's a multiple of page size. Furthermore we want to allocate two extras pages // for the stack guard. To achieve that we do our calculations in number of pages and // convert to bytes last. let page_size = page_size(); @@ -412,30 +405,51 @@ cfg_if! { Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) } } else if #[cfg(any(target_os = "linux", target_os="solaris", target_os = "netbsd"))] { + unsafe fn destroy_pthread_attr(attr: *mut libc::pthread_attr_t) { + let ret = libc::pthread_attr_destroy(attr); + assert!(ret == 0, "pthread_attr_destroy failed with error code: {}", ret); + } + unsafe fn guess_os_stack_limit() -> Option { let mut attr = std::mem::MaybeUninit::::uninit(); - assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0); - assert_eq!(libc::pthread_getattr_np(libc::pthread_self(), - attr.as_mut_ptr()), 0); + if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { + return None; + } + if libc::pthread_getattr_np(libc::pthread_self(), attr.as_mut_ptr()) != 0 { + destroy_pthread_attr(attr.as_mut_ptr()); + return None; + } let mut stackaddr = std::ptr::null_mut(); let mut stacksize = 0; - assert_eq!(libc::pthread_attr_getstack( - attr.as_ptr(), &mut stackaddr, &mut stacksize - ), 0); - assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0); + if libc::pthread_attr_getstack(attr.as_ptr(), &mut stackaddr, &mut stacksize) != 0 { + destroy_pthread_attr(attr.as_mut_ptr()); + return None; + } + destroy_pthread_attr(attr.as_mut_ptr()); Some(stackaddr as usize) } } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] { + unsafe fn destroy_pthread_attr(attr: *mut libc::pthread_attr_t) { + let ret = libc::pthread_attr_destroy(attr); + assert!(ret == 0, "pthread_attr_destroy failed with error code: {}", ret); + } + unsafe fn guess_os_stack_limit() -> Option { let mut attr = std::mem::MaybeUninit::::uninit(); - assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0); - assert_eq!(libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr()), 0); + if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { + return None; + } + if libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr()) != 0 { + destroy_pthread_attr(attr.as_mut_ptr()); + return None; + } let mut stackaddr = std::ptr::null_mut(); let mut stacksize = 0; - assert_eq!(libc::pthread_attr_getstack( - attr.as_ptr(), &mut stackaddr, &mut stacksize - ), 0); - assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0); + if libc::pthread_attr_getstack(attr.as_ptr(), &mut stackaddr, &mut stacksize) != 0 { + destroy_pthread_attr(attr.as_mut_ptr()); + return None; + } + destroy_pthread_attr(attr.as_mut_ptr()); Some(stackaddr as usize) } } else if #[cfg(target_os = "openbsd")] { From afb30f5800d7a9c9f913901b0eb08980d4192de7 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 18 Feb 2025 13:24:30 +0100 Subject: [PATCH 02/16] improve error message in destroy_pthread_attr --- src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index de0825e..014dec3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -407,7 +407,9 @@ cfg_if! { } else if #[cfg(any(target_os = "linux", target_os="solaris", target_os = "netbsd"))] { unsafe fn destroy_pthread_attr(attr: *mut libc::pthread_attr_t) { let ret = libc::pthread_attr_destroy(attr); - assert!(ret == 0, "pthread_attr_destroy failed with error code: {}", ret); + if ret != 0 { + panic!("pthread_attr_destroy failed with error code {}: {}", ret, std::io::Error::last_os_error()); + } } unsafe fn guess_os_stack_limit() -> Option { @@ -431,7 +433,9 @@ cfg_if! { } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] { unsafe fn destroy_pthread_attr(attr: *mut libc::pthread_attr_t) { let ret = libc::pthread_attr_destroy(attr); - assert!(ret == 0, "pthread_attr_destroy failed with error code: {}", ret); + if ret != 0 { + panic!("pthread_attr_destroy failed with error code {}: {}", ret, std::io::Error::last_os_error()); + } } unsafe fn guess_os_stack_limit() -> Option { From aeeb2b55fbe6d8c89b9c345c5ab672bf36e0d101 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 18 Feb 2025 13:40:15 +0100 Subject: [PATCH 03/16] remove code duplication --- src/lib.rs | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 014dec3..9f4b876 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -404,7 +404,14 @@ cfg_if! { ); Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) } - } else if #[cfg(any(target_os = "linux", target_os="solaris", target_os = "netbsd"))] { + } else if #[cfg(any( + target_os = "linux", + target_os = "solaris", + target_os = "netbsd", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "illumos" + ))] { unsafe fn destroy_pthread_attr(attr: *mut libc::pthread_attr_t) { let ret = libc::pthread_attr_destroy(attr); if ret != 0 { @@ -417,42 +424,28 @@ cfg_if! { if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { return None; } - if libc::pthread_getattr_np(libc::pthread_self(), attr.as_mut_ptr()) != 0 { - destroy_pthread_attr(attr.as_mut_ptr()); - return None; - } - let mut stackaddr = std::ptr::null_mut(); - let mut stacksize = 0; - if libc::pthread_attr_getstack(attr.as_ptr(), &mut stackaddr, &mut stacksize) != 0 { - destroy_pthread_attr(attr.as_mut_ptr()); - return None; - } - destroy_pthread_attr(attr.as_mut_ptr()); - Some(stackaddr as usize) - } - } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] { - unsafe fn destroy_pthread_attr(attr: *mut libc::pthread_attr_t) { - let ret = libc::pthread_attr_destroy(attr); - if ret != 0 { - panic!("pthread_attr_destroy failed with error code {}: {}", ret, std::io::Error::last_os_error()); - } - } - unsafe fn guess_os_stack_limit() -> Option { - let mut attr = std::mem::MaybeUninit::::uninit(); - if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { - return None; - } - if libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr()) != 0 { + let success = { + #[cfg(any(target_os = "linux", target_os="solaris", target_os = "netbsd"))] + let res = libc::pthread_getattr_np(libc::pthread_self(), attr.as_mut_ptr()); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] + let res = libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr()); + + res == 0 + }; + + if !success { destroy_pthread_attr(attr.as_mut_ptr()); return None; } + let mut stackaddr = std::ptr::null_mut(); let mut stacksize = 0; if libc::pthread_attr_getstack(attr.as_ptr(), &mut stackaddr, &mut stacksize) != 0 { destroy_pthread_attr(attr.as_mut_ptr()); return None; } + destroy_pthread_attr(attr.as_mut_ptr()); Some(stackaddr as usize) } From c51b55f6daea7c45907b1418a043fd628e68cc71 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 18 Feb 2025 13:58:23 +0100 Subject: [PATCH 04/16] cleanup code and add comments --- src/lib.rs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9f4b876..0dbe8fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -419,34 +419,41 @@ cfg_if! { } } + unsafe fn handle_pthread_err(attr: *mut libc::pthread_attr_t, ret: libc::c_int) -> Option<()> { + if ret != 0 { + destroy_pthread_attr(attr); + None + } else { + Some(()) + } + } + unsafe fn guess_os_stack_limit() -> Option { + // Initialize pthread attributes structure let mut attr = std::mem::MaybeUninit::::uninit(); if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { return None; } - let success = { - #[cfg(any(target_os = "linux", target_os="solaris", target_os = "netbsd"))] - let res = libc::pthread_getattr_np(libc::pthread_self(), attr.as_mut_ptr()); - #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] - let res = libc::pthread_attr_get_np(libc::pthread_self(), attr.as_mut_ptr()); + let pthread_ctx = attr.as_mut_ptr(); - res == 0 - }; + // Linux and BSD use different functions to get attributes of created thread + #[cfg(any(target_os = "linux", target_os="solaris", target_os = "netbsd"))] + let get_attr = libc::pthread_getattr_np; + #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] + let get_attr = libc::pthread_attr_get_np; - if !success { - destroy_pthread_attr(attr.as_mut_ptr()); - return None; - } + // Get current thread's attributes + handle_pthread_err(pthread_ctx, + get_attr(libc::pthread_self(), pthread_ctx))?; + // Get the stack address and size from attributes let mut stackaddr = std::ptr::null_mut(); let mut stacksize = 0; - if libc::pthread_attr_getstack(attr.as_ptr(), &mut stackaddr, &mut stacksize) != 0 { - destroy_pthread_attr(attr.as_mut_ptr()); - return None; - } + handle_pthread_err(pthread_ctx, + libc::pthread_attr_getstack(pthread_ctx, &mut stackaddr, &mut stacksize))?; - destroy_pthread_attr(attr.as_mut_ptr()); + destroy_pthread_attr(pthread_ctx); Some(stackaddr as usize) } } else if #[cfg(target_os = "openbsd")] { From 83c37faddc98e55279ed869f6955a74bbbdbf009 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 18 Feb 2025 14:27:33 +0100 Subject: [PATCH 05/16] Make error handling code easier to read --- src/lib.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0dbe8fc..d080e87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -422,10 +422,9 @@ cfg_if! { unsafe fn handle_pthread_err(attr: *mut libc::pthread_attr_t, ret: libc::c_int) -> Option<()> { if ret != 0 { destroy_pthread_attr(attr); - None - } else { - Some(()) + return None; } + Some(()) } unsafe fn guess_os_stack_limit() -> Option { @@ -438,20 +437,23 @@ cfg_if! { let pthread_ctx = attr.as_mut_ptr(); // Linux and BSD use different functions to get attributes of created thread - #[cfg(any(target_os = "linux", target_os="solaris", target_os = "netbsd"))] - let get_attr = libc::pthread_getattr_np; - #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] - let get_attr = libc::pthread_attr_get_np; + cfg_if! { + if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] { + let get_attr = libc::pthread_attr_get_np; + } else { + let get_attr = libc::pthread_getattr_np; + } + }; // Get current thread's attributes - handle_pthread_err(pthread_ctx, - get_attr(libc::pthread_self(), pthread_ctx))?; + let res = get_attr(libc::pthread_self(), pthread_ctx); + handle_pthread_err(pthread_ctx, res)?; // Get the stack address and size from attributes let mut stackaddr = std::ptr::null_mut(); let mut stacksize = 0; - handle_pthread_err(pthread_ctx, - libc::pthread_attr_getstack(pthread_ctx, &mut stackaddr, &mut stacksize))?; + let res = libc::pthread_attr_getstack(pthread_ctx, &mut stackaddr, &mut stacksize); + handle_pthread_err(pthread_ctx, res)?; destroy_pthread_attr(pthread_ctx); Some(stackaddr as usize) From be70a53a2338ec96d5d9881e6490dbacf73b8490 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 14:16:00 +0100 Subject: [PATCH 06/16] update formatting https://github.com/rust-lang/stacker/pull/116/files#r1961535064 --- src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d080e87..32f4d4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -163,7 +163,12 @@ psm_stack_manipulation! { -1, // Some implementations assert fd = -1 if MAP_ANON is specified 0 ); - assert_ne!(new_stack, libc::MAP_FAILED, "mmap failed to allocate stack: {}", std::io::Error::last_os_error()); + assert_ne!( + new_stack, + libc::MAP_FAILED, + "mmap failed to allocate stack: {}", + std::io::Error::last_os_error() + ); let guard = StackRestoreGuard { new_stack, stack_bytes, From 01fea4f9b2fde7e67e450273df85806fca8faea4 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 14:18:52 +0100 Subject: [PATCH 07/16] put the beautiful unicode apostrophe back https://github.com/rust-lang/stacker/pull/116/files#r1961536979 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 32f4d4e..53b3e8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,7 +220,7 @@ psm_stack_manipulation! { fn _grow(stack_size: usize, callback: &mut dyn FnMut()) { // Calculate a number of pages we want to allocate for the new stack. // For maximum portability we want to produce a stack that is aligned to a page and has - // a size that's a multiple of page size. Furthermore we want to allocate two extras pages + // a size that’s a multiple of page size. Furthermore we want to allocate two extras pages // for the stack guard. To achieve that we do our calculations in number of pages and // convert to bytes last. let page_size = page_size(); From a8852bf8e78c7ae1f26336cb72ebafa8505850b0 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 14:59:30 +0100 Subject: [PATCH 08/16] use one file per guess_os_stack_limit implementation --- src/guess_os_stack_limit/fallback.rs | 4 + src/guess_os_stack_limit/macos.rs | 6 + src/guess_os_stack_limit/mod.rs | 29 +++ src/guess_os_stack_limit/openbsd.rs | 8 + src/guess_os_stack_limit/unix.rs | 20 ++ .../unix_pthread_wrapper.rs | 37 +++ src/guess_os_stack_limit/windows.rs | 30 +++ src/lib.rs | 219 +----------------- 8 files changed, 138 insertions(+), 215 deletions(-) create mode 100644 src/guess_os_stack_limit/fallback.rs create mode 100644 src/guess_os_stack_limit/macos.rs create mode 100644 src/guess_os_stack_limit/mod.rs create mode 100644 src/guess_os_stack_limit/openbsd.rs create mode 100644 src/guess_os_stack_limit/unix.rs create mode 100644 src/guess_os_stack_limit/unix_pthread_wrapper.rs create mode 100644 src/guess_os_stack_limit/windows.rs diff --git a/src/guess_os_stack_limit/fallback.rs b/src/guess_os_stack_limit/fallback.rs new file mode 100644 index 0000000..a812cbc --- /dev/null +++ b/src/guess_os_stack_limit/fallback.rs @@ -0,0 +1,4 @@ +#[inline(always)] +pub unsafe fn guess_os_stack_limit() -> Option { + None +} diff --git a/src/guess_os_stack_limit/macos.rs b/src/guess_os_stack_limit/macos.rs new file mode 100644 index 0000000..42b2851 --- /dev/null +++ b/src/guess_os_stack_limit/macos.rs @@ -0,0 +1,6 @@ +pub unsafe fn guess_os_stack_limit() -> Option { + Some( + libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize + - libc::pthread_get_stacksize_np(libc::pthread_self()) as usize, + ) +} diff --git a/src/guess_os_stack_limit/mod.rs b/src/guess_os_stack_limit/mod.rs new file mode 100644 index 0000000..7b14a34 --- /dev/null +++ b/src/guess_os_stack_limit/mod.rs @@ -0,0 +1,29 @@ +cfg_if! { + if #[cfg(miri)] { + mod fallback; + pub use fallback::guess_os_stack_limit; + } else if #[cfg(windows)] { + mod windows; + pub use windows::guess_os_stack_limit; + } else if #[cfg(any( + target_os = "linux", + target_os = "solaris", + target_os = "netbsd", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "illumos" + ))] { + mod unix; + mod unix_pthread_wrapper; + pub use unix::guess_os_stack_limit; + } else if #[cfg(target_os = "openbsd")] { + mod openbsd; + pub use openbsd::guess_os_stack_limit; + } else if #[cfg(target_os = "macos")] { + mod macos; + pub use macos::guess_os_stack_limit; + } else { + mod fallback; + pub use fallback::guess_os_stack_limit; + } +} diff --git a/src/guess_os_stack_limit/openbsd.rs b/src/guess_os_stack_limit/openbsd.rs new file mode 100644 index 0000000..174efb2 --- /dev/null +++ b/src/guess_os_stack_limit/openbsd.rs @@ -0,0 +1,8 @@ +pub unsafe fn guess_os_stack_limit() -> Option { + let mut stackinfo = std::mem::MaybeUninit::::uninit(); + assert_eq!( + libc::pthread_stackseg_np(libc::pthread_self(), stackinfo.as_mut_ptr()), + 0 + ); + Some(stackinfo.assume_init().ss_sp as usize - stackinfo.assume_init().ss_size) +} diff --git a/src/guess_os_stack_limit/unix.rs b/src/guess_os_stack_limit/unix.rs new file mode 100644 index 0000000..6e1eced --- /dev/null +++ b/src/guess_os_stack_limit/unix.rs @@ -0,0 +1,20 @@ +use super::unix_pthread_wrapper::PthreadAttr; + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] +use libc::pthread_attr_get_np as get_attr; +#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "netbsd"))] +use libc::pthread_getattr_np as get_attr; + +pub unsafe fn guess_os_stack_limit() -> Option { + let mut attr = PthreadAttr::new()?; + + let res = get_attr(libc::pthread_self(), attr.as_mut_ptr()); + attr.handle_pthread_err(res)?; + + let mut stackaddr = std::ptr::null_mut(); + let mut stacksize = 0; + let res = libc::pthread_attr_getstack(attr.as_mut_ptr(), &mut stackaddr, &mut stacksize); + attr.handle_pthread_err(res)?; + + Some(stackaddr as usize) +} diff --git a/src/guess_os_stack_limit/unix_pthread_wrapper.rs b/src/guess_os_stack_limit/unix_pthread_wrapper.rs new file mode 100644 index 0000000..5d9afba --- /dev/null +++ b/src/guess_os_stack_limit/unix_pthread_wrapper.rs @@ -0,0 +1,37 @@ +pub(super) struct PthreadAttr(std::mem::MaybeUninit); + +impl Drop for PthreadAttr { + fn drop(&mut self) { + unsafe { + let ret = libc::pthread_attr_destroy(self.0.as_mut_ptr()); + if ret != 0 { + let err = std::io::Error::last_os_error(); + panic!( + "pthread_attr_destroy failed with error code {}: {}", + ret, err + ); + } + } + } +} + +impl PthreadAttr { + pub unsafe fn new() -> Option { + let mut attr = std::mem::MaybeUninit::::uninit(); + if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { + return None; + } + Some(PthreadAttr(attr)) + } + + pub unsafe fn handle_pthread_err(&self, ret: libc::c_int) -> Option<()> { + if ret != 0 { + return None; + } + Some(()) + } + + pub fn as_mut_ptr(&mut self) -> *mut libc::pthread_attr_t { + self.0.as_mut_ptr() + } +} diff --git a/src/guess_os_stack_limit/windows.rs b/src/guess_os_stack_limit/windows.rs new file mode 100644 index 0000000..51a05e5 --- /dev/null +++ b/src/guess_os_stack_limit/windows.rs @@ -0,0 +1,30 @@ +use libc::c_void; +use std::io; +use std::ptr; +use windows_sys::Win32::Foundation::BOOL; +use windows_sys::Win32::System::Memory::VirtualQuery; +use windows_sys::Win32::System::Threading::*; + +#[inline(always)] +fn get_thread_stack_guarantee() -> usize { + let min_guarantee = if cfg!(target_pointer_width = "32") { + 0x1000 + } else { + 0x2000 + }; + let mut stack_guarantee = 0; + unsafe { SetThreadStackGuarantee(&mut stack_guarantee) }; + std::cmp::max(stack_guarantee, min_guarantee) as usize + 0x1000 +} + +#[inline(always)] +pub unsafe fn guess_os_stack_limit() -> Option { + type QueryT = windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION; + let mut mi = std::mem::MaybeUninit::::uninit(); + VirtualQuery( + psm::stack_pointer() as *const _, + mi.as_mut_ptr(), + std::mem::size_of::() as usize, + ); + Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) +} diff --git a/src/lib.rs b/src/lib.rs index 53b3e8a..3afb80b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,8 @@ extern crate windows_sys; #[macro_use] extern crate psm; +mod guess_os_stack_limit; + use std::cell::Cell; /// Grows the call stack if necessary. @@ -116,7 +118,7 @@ psm_stack_information!( thread_local! { static STACK_LIMIT: Cell> = Cell::new(unsafe { - guess_os_stack_limit() + guess_os_stack_limit::guess_os_stack_limit() }) } @@ -220,7 +222,7 @@ psm_stack_manipulation! { fn _grow(stack_size: usize, callback: &mut dyn FnMut()) { // Calculate a number of pages we want to allocate for the new stack. // For maximum portability we want to produce a stack that is aligned to a page and has - // a size that’s a multiple of page size. Furthermore we want to allocate two extras pages + // a size that's a multiple of page size. Furthermore we want to allocate two extras pages // for the stack guard. To achieve that we do our calculations in number of pages and // convert to bytes last. let page_size = page_size(); @@ -271,216 +273,3 @@ psm_stack_manipulation! { } } } - -cfg_if! { - if #[cfg(miri)] { - // Miri doesn't have a stack limit - #[inline(always)] - unsafe fn guess_os_stack_limit() -> Option { - None - } - } else if #[cfg(windows)] { - use std::ptr; - use std::io; - use libc::c_void; - use windows_sys::Win32::System::Threading::{SwitchToFiber, IsThreadAFiber, ConvertThreadToFiber, - CreateFiber, DeleteFiber, ConvertFiberToThread, SetThreadStackGuarantee - }; - use windows_sys::Win32::Foundation::BOOL; - use windows_sys::Win32::System::Memory::VirtualQuery; - - // Make sure the libstacker.a (implemented in C) is linked. - // See https://github.com/rust-lang/rust/issues/65610 - #[link(name="stacker")] - extern { - fn __stacker_get_current_fiber() -> *mut c_void; - } - - struct FiberInfo { - callback: std::mem::MaybeUninit, - panic: Option>, - parent_fiber: *mut c_void, - } - - unsafe extern "system" fn fiber_proc(data: *mut c_void) { - // This function is the entry point to our inner fiber, and as argument we get an - // instance of `FiberInfo`. We will set-up the "runtime" for the callback and execute - // it. - let data = &mut *(data as *mut FiberInfo); - let old_stack_limit = get_stack_limit(); - set_stack_limit(guess_os_stack_limit()); - let callback = data.callback.as_ptr(); - data.panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(callback.read())).err(); - - // Restore to the previous Fiber - set_stack_limit(old_stack_limit); - SwitchToFiber(data.parent_fiber); - } - - fn _grow(stack_size: usize, callback: &mut dyn FnMut()) { - // Fibers (or stackful coroutines) is the only official way to create new stacks on the - // same thread on Windows. So in order to extend the stack we create fiber and switch - // to it so we can use it's stack. After running `callback` within our fiber, we switch - // back to the current stack and destroy the fiber and its associated stack. - unsafe { - let was_fiber = IsThreadAFiber() == 1 as BOOL; - let mut data = FiberInfo { - callback: std::mem::MaybeUninit::new(callback), - panic: None, - parent_fiber: { - if was_fiber { - // Get a handle to the current fiber. We need to use a C implementation - // for this as GetCurrentFiber is an header only function. - __stacker_get_current_fiber() - } else { - // Convert the current thread to a fiber, so we are able to switch back - // to the current stack. Threads coverted to fibers still act like - // regular threads, but they have associated fiber data. We later - // convert it back to a regular thread and free the fiber data. - ConvertThreadToFiber(ptr::null_mut()) - } - }, - }; - - if data.parent_fiber.is_null() { - panic!("unable to convert thread to fiber: {}", io::Error::last_os_error()); - } - - let fiber = CreateFiber( - stack_size as usize, - Some(fiber_proc::<&mut dyn FnMut()>), - &mut data as *mut FiberInfo<&mut dyn FnMut()> as *mut _, - ); - if fiber.is_null() { - panic!("unable to allocate fiber: {}", io::Error::last_os_error()); - } - - // Switch to the fiber we created. This changes stacks and starts executing - // fiber_proc on it. fiber_proc will run `callback` and then switch back to run the - // next statement. - SwitchToFiber(fiber); - DeleteFiber(fiber); - - // Clean-up. - if !was_fiber && ConvertFiberToThread() == 0 { - // FIXME: Perhaps should not panic here? - panic!("unable to convert back to thread: {}", io::Error::last_os_error()); - } - - if let Some(p) = data.panic { - std::panic::resume_unwind(p); - } - } - } - - #[inline(always)] - fn get_thread_stack_guarantee() -> usize { - let min_guarantee = if cfg!(target_pointer_width = "32") { - 0x1000 - } else { - 0x2000 - }; - let mut stack_guarantee = 0; - unsafe { - // Read the current thread stack guarantee - // This is the stack reserved for stack overflow - // exception handling. - // This doesn't return the true value so we need - // some further logic to calculate the real stack - // guarantee. This logic is what is used on x86-32 and - // x86-64 Windows 10. Other versions and platforms may differ - SetThreadStackGuarantee(&mut stack_guarantee) - }; - std::cmp::max(stack_guarantee, min_guarantee) as usize + 0x1000 - } - - #[inline(always)] - unsafe fn guess_os_stack_limit() -> Option { - // Query the allocation which contains our stack pointer in order - // to discover the size of the stack - // - // FIXME: we could read stack base from the TIB, specifically the 3rd element of it. - type QueryT = windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION; - let mut mi = std::mem::MaybeUninit::::uninit(); - VirtualQuery( - psm::stack_pointer() as *const _, - mi.as_mut_ptr(), - std::mem::size_of::() as usize, - ); - Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) - } - } else if #[cfg(any( - target_os = "linux", - target_os = "solaris", - target_os = "netbsd", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "illumos" - ))] { - unsafe fn destroy_pthread_attr(attr: *mut libc::pthread_attr_t) { - let ret = libc::pthread_attr_destroy(attr); - if ret != 0 { - panic!("pthread_attr_destroy failed with error code {}: {}", ret, std::io::Error::last_os_error()); - } - } - - unsafe fn handle_pthread_err(attr: *mut libc::pthread_attr_t, ret: libc::c_int) -> Option<()> { - if ret != 0 { - destroy_pthread_attr(attr); - return None; - } - Some(()) - } - - unsafe fn guess_os_stack_limit() -> Option { - // Initialize pthread attributes structure - let mut attr = std::mem::MaybeUninit::::uninit(); - if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { - return None; - } - - let pthread_ctx = attr.as_mut_ptr(); - - // Linux and BSD use different functions to get attributes of created thread - cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] { - let get_attr = libc::pthread_attr_get_np; - } else { - let get_attr = libc::pthread_getattr_np; - } - }; - - // Get current thread's attributes - let res = get_attr(libc::pthread_self(), pthread_ctx); - handle_pthread_err(pthread_ctx, res)?; - - // Get the stack address and size from attributes - let mut stackaddr = std::ptr::null_mut(); - let mut stacksize = 0; - let res = libc::pthread_attr_getstack(pthread_ctx, &mut stackaddr, &mut stacksize); - handle_pthread_err(pthread_ctx, res)?; - - destroy_pthread_attr(pthread_ctx); - Some(stackaddr as usize) - } - } else if #[cfg(target_os = "openbsd")] { - unsafe fn guess_os_stack_limit() -> Option { - let mut stackinfo = std::mem::MaybeUninit::::uninit(); - assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), stackinfo.as_mut_ptr()), 0); - Some(stackinfo.assume_init().ss_sp as usize - stackinfo.assume_init().ss_size) - } - } else if #[cfg(target_os = "macos")] { - unsafe fn guess_os_stack_limit() -> Option { - Some(libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize - - libc::pthread_get_stacksize_np(libc::pthread_self()) as usize) - } - } else { - // fallback for other platforms is to always increase the stack if we're on - // the root stack. After we increased the stack once, we know the new stack - // size and don't need this pessimization anymore - #[inline(always)] - unsafe fn guess_os_stack_limit() -> Option { - None - } - } -} From ddf1164fa41f8fc5ad38dd1f78009cefe2805857 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 15:00:39 +0100 Subject: [PATCH 09/16] fix error handling on openbsd too --- src/guess_os_stack_limit/openbsd.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/guess_os_stack_limit/openbsd.rs b/src/guess_os_stack_limit/openbsd.rs index 174efb2..3a6dd71 100644 --- a/src/guess_os_stack_limit/openbsd.rs +++ b/src/guess_os_stack_limit/openbsd.rs @@ -1,8 +1,8 @@ pub unsafe fn guess_os_stack_limit() -> Option { let mut stackinfo = std::mem::MaybeUninit::::uninit(); - assert_eq!( - libc::pthread_stackseg_np(libc::pthread_self(), stackinfo.as_mut_ptr()), - 0 - ); + let res = libc::pthread_stackseg_np(libc::pthread_self(), stackinfo.as_mut_ptr()); + if res != 0 { + return None; + } Some(stackinfo.assume_init().ss_sp as usize - stackinfo.assume_init().ss_size) } From f33c9cf1fe966c51785852cb217b9f7a5b89f820 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 15:06:15 +0100 Subject: [PATCH 10/16] beautiful unicode apostrophe --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3afb80b..028aac1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -222,7 +222,7 @@ psm_stack_manipulation! { fn _grow(stack_size: usize, callback: &mut dyn FnMut()) { // Calculate a number of pages we want to allocate for the new stack. // For maximum portability we want to produce a stack that is aligned to a page and has - // a size that's a multiple of page size. Furthermore we want to allocate two extras pages + // a size that’s a multiple of page size. Furthermore we want to allocate two extras pages // for the stack guard. To achieve that we do our calculations in number of pages and // convert to bytes last. let page_size = page_size(); From 661b209aba1837fa43beaec0f935f22c78e67914 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 15:07:33 +0100 Subject: [PATCH 11/16] format --- src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 028aac1..0277883 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,7 +195,12 @@ psm_stack_manipulation! { } else { -1 }; - assert_ne!(result, -1, "mprotect/mmap failed: {}", std::io::Error::last_os_error()); + assert_ne!( + result, + -1, + "mprotect/mmap failed: {}", + std::io::Error::last_os_error() + ); guard } } From 7d65f5fd670f2de74d307342aee70bdb9967f7db Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 16:10:53 +0100 Subject: [PATCH 12/16] fix windows build --- .../fallback.rs | 0 .../macos.rs | 0 src/{guess_os_stack_limit => backends}/mod.rs | 2 +- .../openbsd.rs | 0 .../unix.rs | 0 .../unix_pthread_wrapper.rs | 0 src/backends/windows.rs | 136 ++++++++++++++++++ src/guess_os_stack_limit/windows.rs | 30 ---- src/lib.rs | 6 +- 9 files changed, 141 insertions(+), 33 deletions(-) rename src/{guess_os_stack_limit => backends}/fallback.rs (100%) rename src/{guess_os_stack_limit => backends}/macos.rs (100%) rename src/{guess_os_stack_limit => backends}/mod.rs (96%) rename src/{guess_os_stack_limit => backends}/openbsd.rs (100%) rename src/{guess_os_stack_limit => backends}/unix.rs (100%) rename src/{guess_os_stack_limit => backends}/unix_pthread_wrapper.rs (100%) create mode 100644 src/backends/windows.rs delete mode 100644 src/guess_os_stack_limit/windows.rs diff --git a/src/guess_os_stack_limit/fallback.rs b/src/backends/fallback.rs similarity index 100% rename from src/guess_os_stack_limit/fallback.rs rename to src/backends/fallback.rs diff --git a/src/guess_os_stack_limit/macos.rs b/src/backends/macos.rs similarity index 100% rename from src/guess_os_stack_limit/macos.rs rename to src/backends/macos.rs diff --git a/src/guess_os_stack_limit/mod.rs b/src/backends/mod.rs similarity index 96% rename from src/guess_os_stack_limit/mod.rs rename to src/backends/mod.rs index 7b14a34..2821cfa 100644 --- a/src/guess_os_stack_limit/mod.rs +++ b/src/backends/mod.rs @@ -3,7 +3,7 @@ cfg_if! { mod fallback; pub use fallback::guess_os_stack_limit; } else if #[cfg(windows)] { - mod windows; + pub(crate) mod windows; pub use windows::guess_os_stack_limit; } else if #[cfg(any( target_os = "linux", diff --git a/src/guess_os_stack_limit/openbsd.rs b/src/backends/openbsd.rs similarity index 100% rename from src/guess_os_stack_limit/openbsd.rs rename to src/backends/openbsd.rs diff --git a/src/guess_os_stack_limit/unix.rs b/src/backends/unix.rs similarity index 100% rename from src/guess_os_stack_limit/unix.rs rename to src/backends/unix.rs diff --git a/src/guess_os_stack_limit/unix_pthread_wrapper.rs b/src/backends/unix_pthread_wrapper.rs similarity index 100% rename from src/guess_os_stack_limit/unix_pthread_wrapper.rs rename to src/backends/unix_pthread_wrapper.rs diff --git a/src/backends/windows.rs b/src/backends/windows.rs new file mode 100644 index 0000000..401f6fc --- /dev/null +++ b/src/backends/windows.rs @@ -0,0 +1,136 @@ +use libc::c_void; +use std::io; +use std::ptr; +use windows_sys::Win32::Foundation::BOOL; +use windows_sys::Win32::System::Memory::VirtualQuery; +use windows_sys::Win32::System::Threading::{ + ConvertFiberToThread, ConvertThreadToFiber, CreateFiber, DeleteFiber, IsThreadAFiber, + SetThreadStackGuarantee, SwitchToFiber, +}; + +// Make sure the libstacker.a (implemented in C) is linked. +// See https://github.com/rust-lang/rust/issues/65610 +#[link(name = "stacker")] +extern "C" { + fn __stacker_get_current_fiber() -> *mut c_void; +} + +struct FiberInfo { + callback: std::mem::MaybeUninit, + panic: Option>, + parent_fiber: *mut c_void, +} + +unsafe extern "system" fn fiber_proc(data: *mut c_void) { + // This function is the entry point to our inner fiber, and as argument we get an + // instance of `FiberInfo`. We will set-up the "runtime" for the callback and execute + // it. + let data = &mut *(data as *mut FiberInfo); + let old_stack_limit = crate::get_stack_limit(); + crate::set_stack_limit(guess_os_stack_limit()); + let callback = data.callback.as_ptr(); + data.panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(callback.read())).err(); + + // Restore to the previous Fiber + crate::set_stack_limit(old_stack_limit); + SwitchToFiber(data.parent_fiber); +} + +pub fn _grow(stack_size: usize, callback: &mut dyn FnMut()) { + // Fibers (or stackful coroutines) is the only official way to create new stacks on the + // same thread on Windows. So in order to extend the stack we create fiber and switch + // to it so we can use it's stack. After running `callback` within our fiber, we switch + // back to the current stack and destroy the fiber and its associated stack. + unsafe { + let was_fiber = IsThreadAFiber() == 1 as BOOL; + let mut data = FiberInfo { + callback: std::mem::MaybeUninit::new(callback), + panic: None, + parent_fiber: { + if was_fiber { + // Get a handle to the current fiber. We need to use a C implementation + // for this as GetCurrentFiber is an header only function. + __stacker_get_current_fiber() + } else { + // Convert the current thread to a fiber, so we are able to switch back + // to the current stack. Threads coverted to fibers still act like + // regular threads, but they have associated fiber data. We later + // convert it back to a regular thread and free the fiber data. + ConvertThreadToFiber(ptr::null_mut()) + } + }, + }; + + if data.parent_fiber.is_null() { + panic!( + "unable to convert thread to fiber: {}", + io::Error::last_os_error() + ); + } + + let fiber = CreateFiber( + stack_size as usize, + Some(fiber_proc::<&mut dyn FnMut()>), + &mut data as *mut FiberInfo<&mut dyn FnMut()> as *mut _, + ); + if fiber.is_null() { + panic!("unable to allocate fiber: {}", io::Error::last_os_error()); + } + + // Switch to the fiber we created. This changes stacks and starts executing + // fiber_proc on it. fiber_proc will run `callback` and then switch back to run the + // next statement. + SwitchToFiber(fiber); + DeleteFiber(fiber); + + // Clean-up. + if !was_fiber && ConvertFiberToThread() == 0 { + // FIXME: Perhaps should not panic here? + panic!( + "unable to convert back to thread: {}", + io::Error::last_os_error() + ); + } + + if let Some(p) = data.panic { + std::panic::resume_unwind(p); + } + } +} + +#[inline(always)] +fn get_thread_stack_guarantee() -> usize { + let min_guarantee = if cfg!(target_pointer_width = "32") { + 0x1000 + } else { + 0x2000 + }; + let mut stack_guarantee = 0; + unsafe { + // Read the current thread stack guarantee + // This is the stack reserved for stack overflow + // exception handling. + // This doesn't return the true value so we need + // some further logic to calculate the real stack + // guarantee. This logic is what is used on x86-32 and + // x86-64 Windows 10. Other versions and platforms may differ + SetThreadStackGuarantee(&mut stack_guarantee) + }; + std::cmp::max(stack_guarantee, min_guarantee) as usize + 0x1000 +} + +#[inline(always)] +pub unsafe fn guess_os_stack_limit() -> Option { + // Query the allocation which contains our stack pointer in order + // to discover the size of the stack + // + // FIXME: we could read stack base from the TIB, specifically the 3rd element of it. + type QueryT = windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION; + let mut mi = std::mem::MaybeUninit::::uninit(); + VirtualQuery( + psm::stack_pointer() as *const _, + mi.as_mut_ptr(), + std::mem::size_of::() as usize, + ); + Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) +} diff --git a/src/guess_os_stack_limit/windows.rs b/src/guess_os_stack_limit/windows.rs deleted file mode 100644 index 51a05e5..0000000 --- a/src/guess_os_stack_limit/windows.rs +++ /dev/null @@ -1,30 +0,0 @@ -use libc::c_void; -use std::io; -use std::ptr; -use windows_sys::Win32::Foundation::BOOL; -use windows_sys::Win32::System::Memory::VirtualQuery; -use windows_sys::Win32::System::Threading::*; - -#[inline(always)] -fn get_thread_stack_guarantee() -> usize { - let min_guarantee = if cfg!(target_pointer_width = "32") { - 0x1000 - } else { - 0x2000 - }; - let mut stack_guarantee = 0; - unsafe { SetThreadStackGuarantee(&mut stack_guarantee) }; - std::cmp::max(stack_guarantee, min_guarantee) as usize + 0x1000 -} - -#[inline(always)] -pub unsafe fn guess_os_stack_limit() -> Option { - type QueryT = windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION; - let mut mi = std::mem::MaybeUninit::::uninit(); - VirtualQuery( - psm::stack_pointer() as *const _, - mi.as_mut_ptr(), - std::mem::size_of::() as usize, - ); - Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) -} diff --git a/src/lib.rs b/src/lib.rs index 0277883..3f98ce8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ extern crate windows_sys; #[macro_use] extern crate psm; -mod guess_os_stack_limit; +mod backends; use std::cell::Cell; @@ -118,7 +118,7 @@ psm_stack_information!( thread_local! { static STACK_LIMIT: Cell> = Cell::new(unsafe { - guess_os_stack_limit::guess_os_stack_limit() + backends::guess_os_stack_limit() }) } @@ -276,5 +276,7 @@ psm_stack_manipulation! { let _ = stack_size; callback(); } + #[cfg(windows)] + use backends::windows::_grow; } } From 5837340d802275c7013548613fe4a90bedd5255a Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 16:14:02 +0100 Subject: [PATCH 13/16] remove unused unsafe --- src/backends/unix_pthread_wrapper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/unix_pthread_wrapper.rs b/src/backends/unix_pthread_wrapper.rs index 5d9afba..8d18e69 100644 --- a/src/backends/unix_pthread_wrapper.rs +++ b/src/backends/unix_pthread_wrapper.rs @@ -24,7 +24,7 @@ impl PthreadAttr { Some(PthreadAttr(attr)) } - pub unsafe fn handle_pthread_err(&self, ret: libc::c_int) -> Option<()> { + pub fn handle_pthread_err(&self, ret: libc::c_int) -> Option<()> { if ret != 0 { return None; } From 0c4110e4c4b0706b23b04e54033c33fe380a4fc6 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 16:26:20 +0100 Subject: [PATCH 14/16] fix error handling on windows VirtualQuery is a faillible function that was treated as infaillible https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualquery --- src/backends/windows.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backends/windows.rs b/src/backends/windows.rs index 401f6fc..c8158ee 100644 --- a/src/backends/windows.rs +++ b/src/backends/windows.rs @@ -127,10 +127,13 @@ pub unsafe fn guess_os_stack_limit() -> Option { // FIXME: we could read stack base from the TIB, specifically the 3rd element of it. type QueryT = windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION; let mut mi = std::mem::MaybeUninit::::uninit(); - VirtualQuery( + let res = VirtualQuery( psm::stack_pointer() as *const _, mi.as_mut_ptr(), std::mem::size_of::() as usize, ); + if res == 0 { + return None; + } Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) } From 53918d0c235922a4531c6b2906890aff88c8db85 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 16:30:53 +0100 Subject: [PATCH 15/16] SetThreadStackGuarantee can also error on windows --- src/backends/windows.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/backends/windows.rs b/src/backends/windows.rs index c8158ee..69c7613 100644 --- a/src/backends/windows.rs +++ b/src/backends/windows.rs @@ -99,7 +99,7 @@ pub fn _grow(stack_size: usize, callback: &mut dyn FnMut()) { } #[inline(always)] -fn get_thread_stack_guarantee() -> usize { +fn get_thread_stack_guarantee() -> Option { let min_guarantee = if cfg!(target_pointer_width = "32") { 0x1000 } else { @@ -114,9 +114,12 @@ fn get_thread_stack_guarantee() -> usize { // some further logic to calculate the real stack // guarantee. This logic is what is used on x86-32 and // x86-64 Windows 10. Other versions and platforms may differ - SetThreadStackGuarantee(&mut stack_guarantee) + let ret = SetThreadStackGuarantee(&mut stack_guarantee); + if ret == 0 { + return None; + } }; - std::cmp::max(stack_guarantee, min_guarantee) as usize + 0x1000 + Some(std::cmp::max(stack_guarantee, min_guarantee) as usize + 0x1000) } #[inline(always)] @@ -135,5 +138,5 @@ pub unsafe fn guess_os_stack_limit() -> Option { if res == 0 { return None; } - Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) + Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee()? + 0x1000) } From d9a123bef82847be6cec174cc9469cdd6c438732 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 19 Feb 2025 16:40:25 +0100 Subject: [PATCH 16/16] simplify pthread error handling --- src/backends/mod.rs | 1 - src/backends/unix.rs | 50 ++++++++++++++++++++++++---- src/backends/unix_pthread_wrapper.rs | 37 -------------------- 3 files changed, 44 insertions(+), 44 deletions(-) delete mode 100644 src/backends/unix_pthread_wrapper.rs diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 2821cfa..8da895d 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -14,7 +14,6 @@ cfg_if! { target_os = "illumos" ))] { mod unix; - mod unix_pthread_wrapper; pub use unix::guess_os_stack_limit; } else if #[cfg(target_os = "openbsd")] { mod openbsd; diff --git a/src/backends/unix.rs b/src/backends/unix.rs index 6e1eced..21ce3b1 100644 --- a/src/backends/unix.rs +++ b/src/backends/unix.rs @@ -1,5 +1,3 @@ -use super::unix_pthread_wrapper::PthreadAttr; - #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "illumos"))] use libc::pthread_attr_get_np as get_attr; #[cfg(any(target_os = "linux", target_os = "solaris", target_os = "netbsd"))] @@ -8,13 +6,53 @@ use libc::pthread_getattr_np as get_attr; pub unsafe fn guess_os_stack_limit() -> Option { let mut attr = PthreadAttr::new()?; - let res = get_attr(libc::pthread_self(), attr.as_mut_ptr()); - attr.handle_pthread_err(res)?; + handle_pthread_err(get_attr(libc::pthread_self(), attr.as_mut_ptr()))?; let mut stackaddr = std::ptr::null_mut(); let mut stacksize = 0; - let res = libc::pthread_attr_getstack(attr.as_mut_ptr(), &mut stackaddr, &mut stacksize); - attr.handle_pthread_err(res)?; + handle_pthread_err(libc::pthread_attr_getstack( + attr.as_mut_ptr(), + &mut stackaddr, + &mut stacksize, + ))?; Some(stackaddr as usize) } + +struct PthreadAttr(std::mem::MaybeUninit); + +impl Drop for PthreadAttr { + fn drop(&mut self) { + unsafe { + let ret = libc::pthread_attr_destroy(self.0.as_mut_ptr()); + if ret != 0 { + let err = std::io::Error::last_os_error(); + panic!( + "pthread_attr_destroy failed with error code {}: {}", + ret, err + ); + } + } + } +} + +fn handle_pthread_err(ret: libc::c_int) -> Option<()> { + if ret != 0 { + return None; + } + Some(()) +} + +impl PthreadAttr { + unsafe fn new() -> Option { + let mut attr = std::mem::MaybeUninit::::uninit(); + if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { + return None; + } + Some(PthreadAttr(attr)) + } + + fn as_mut_ptr(&mut self) -> *mut libc::pthread_attr_t { + self.0.as_mut_ptr() + } +} diff --git a/src/backends/unix_pthread_wrapper.rs b/src/backends/unix_pthread_wrapper.rs deleted file mode 100644 index 8d18e69..0000000 --- a/src/backends/unix_pthread_wrapper.rs +++ /dev/null @@ -1,37 +0,0 @@ -pub(super) struct PthreadAttr(std::mem::MaybeUninit); - -impl Drop for PthreadAttr { - fn drop(&mut self) { - unsafe { - let ret = libc::pthread_attr_destroy(self.0.as_mut_ptr()); - if ret != 0 { - let err = std::io::Error::last_os_error(); - panic!( - "pthread_attr_destroy failed with error code {}: {}", - ret, err - ); - } - } - } -} - -impl PthreadAttr { - pub unsafe fn new() -> Option { - let mut attr = std::mem::MaybeUninit::::uninit(); - if libc::pthread_attr_init(attr.as_mut_ptr()) != 0 { - return None; - } - Some(PthreadAttr(attr)) - } - - pub fn handle_pthread_err(&self, ret: libc::c_int) -> Option<()> { - if ret != 0 { - return None; - } - Some(()) - } - - pub fn as_mut_ptr(&mut self) -> *mut libc::pthread_attr_t { - self.0.as_mut_ptr() - } -}