Skip to content

Commit 3f928e1

Browse files
Dynamically size sigaltstk in std
On modern Linux with Intel AMX and 1KiB matrices, Arm SVE with potentially 2KiB vectors, and RISCV Vectors with up to 16KiB vectors, we must handle dynamic signal stack sizes. We can do so unconditionally by using getauxval, but assuming it may return 0 as an answer, thus falling back to the old constant if needed.
1 parent 71f71a5 commit 3f928e1

File tree

1 file changed

+39
-11
lines changed

1 file changed

+39
-11
lines changed

library/std/src/sys/unix/stack_overflow.rs

+39-11
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ mod imp {
5050
#[cfg(all(target_os = "linux", target_env = "gnu"))]
5151
use libc::{mmap64, munmap};
5252
use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL};
53-
use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
53+
use libc::{sigaltstack, SS_DISABLE};
5454
use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV};
5555

5656
use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
@@ -129,28 +129,36 @@ mod imp {
129129
drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed));
130130
}
131131

132-
unsafe fn get_stackp() -> *mut libc::c_void {
132+
unsafe fn get_stack() -> libc::stack_t {
133133
// OpenBSD requires this flag for stack mapping
134134
// otherwise the said mapping will fail as a no-op on most systems
135135
// and has a different meaning on FreeBSD
136136
#[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",))]
137137
let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
138138
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",)))]
139139
let flags = MAP_PRIVATE | MAP_ANON;
140-
let stackp =
141-
mmap64(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0);
140+
141+
let sigstack_size = sigstack_size();
142+
let page_size = page_size();
143+
144+
let stackp = mmap64(
145+
ptr::null_mut(),
146+
sigstack_size + page_size,
147+
PROT_READ | PROT_WRITE,
148+
flags,
149+
-1,
150+
0,
151+
);
142152
if stackp == MAP_FAILED {
143153
panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
144154
}
145-
let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE);
155+
let guard_result = libc::mprotect(stackp, page_size, PROT_NONE);
146156
if guard_result != 0 {
147157
panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error());
148158
}
149-
stackp.add(page_size())
150-
}
159+
let stackp = stackp.add(page_size);
151160

152-
unsafe fn get_stack() -> libc::stack_t {
153-
libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
161+
libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size }
154162
}
155163

156164
pub unsafe fn make_handler() -> Handler {
@@ -171,21 +179,41 @@ mod imp {
171179

172180
pub unsafe fn drop_handler(data: *mut libc::c_void) {
173181
if !data.is_null() {
182+
let sigstack_size = sigstack_size();
183+
let page_size = page_size();
174184
let stack = libc::stack_t {
175185
ss_sp: ptr::null_mut(),
176186
ss_flags: SS_DISABLE,
177187
// Workaround for bug in macOS implementation of sigaltstack
178188
// UNIX2003 which returns ENOMEM when disabling a stack while
179189
// passing ss_size smaller than MINSIGSTKSZ. According to POSIX
180190
// both ss_sp and ss_size should be ignored in this case.
181-
ss_size: SIGSTKSZ,
191+
ss_size: sigstack_size,
182192
};
183193
sigaltstack(&stack, ptr::null_mut());
184194
// We know from `get_stackp` that the alternate stack we installed is part of a mapping
185195
// that started one page earlier, so walk back a page and unmap from there.
186-
munmap(data.sub(page_size()), SIGSTKSZ + page_size());
196+
munmap(data.sub(page_size), sigstack_size + page_size);
187197
}
188198
}
199+
200+
/// Modern kernels on modern hardware can have dynamic signal stack sizes.
201+
#[cfg(any(target_os = "linux", target_os = "android"))]
202+
fn sigstack_size() -> usize {
203+
// FIXME: reuse const from libc when available?
204+
const AT_MINSIGSTKSZ: crate::ffi::c_ulong = 51;
205+
let dynamic_sigstksz = unsafe { libc::getauxval(AT_MINSIGSTKSZ) };
206+
// If getauxval couldn't find the entry, it returns 0,
207+
// so take the higher of the "constant" and auxval.
208+
// This transparently supports older kernels which don't provide AT_MINSIGSTKSZ
209+
libc::SIGSTKSZ.max(dynamic_sigstksz as _)
210+
}
211+
212+
/// Not all OS support hardware where this is needed.
213+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
214+
fn sigstack_size() -> usize {
215+
libc::SIGSTKSZ
216+
}
189217
}
190218

191219
#[cfg(not(any(

0 commit comments

Comments
 (0)