Skip to content

Commit a5e5bc6

Browse files
Add regression test for debug thread stack size
Verifies that a Python thread started without an explicit threading.stack_size() gets DEFAULT_THREAD_STACK_SIZE (8 MiB) in debug builds instead of Rust's 2 MiB std default, which overflowed and crashed in the SSL test server (#7941). The test reads back the spawned thread's actual stack size via pthread, fails before the fix and passes after it.
1 parent b9483d7 commit a5e5bc6

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

‎crates/vm/src/stdlib/_thread.rs‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,4 +1790,59 @@ pub(crate) mod _thread {
17901790

17911791
Ok(handle_clone)
17921792
}
1793+
1794+
#[cfg(test)]
1795+
mod tests {
1796+
#[cfg(all(debug_assertions, any(target_os = "linux", target_os = "macos")))]
1797+
use super::*;
1798+
#[cfg(all(debug_assertions, any(target_os = "linux", target_os = "macos")))]
1799+
use crate::Interpreter;
1800+
1801+
/// Regression test for #7941: a Python thread started without an
1802+
/// explicit `threading.stack_size()` must not run on Rust's 2 MiB
1803+
/// std default in debug builds, where the call chains the stdlib
1804+
/// runs on helper threads (e.g. the SSL test server) overflowed it
1805+
/// and crashed the process. Before the patch this observed the
1806+
/// Rust default and failed; after the patch it observes
1807+
/// [`DEFAULT_THREAD_STACK_SIZE`].
1808+
#[test]
1809+
#[cfg(all(debug_assertions, any(target_os = "linux", target_os = "macos")))]
1810+
fn default_python_thread_stack_size_debug() {
1811+
Interpreter::without_stdlib(Default::default()).enter(|vm| {
1812+
// No user-configured stack size: the debug default must apply.
1813+
assert_eq!(vm.state.stacksize.load(), 0);
1814+
let builder = apply_thread_stack_size(thread::Builder::new(), vm);
1815+
let stack_size = builder
1816+
.spawn(current_thread_stack_size)
1817+
.expect("failed to spawn thread")
1818+
.join()
1819+
.expect("thread panicked");
1820+
assert!(
1821+
stack_size >= DEFAULT_THREAD_STACK_SIZE,
1822+
"Python thread stack size is {stack_size} bytes, expected at least {DEFAULT_THREAD_STACK_SIZE}"
1823+
);
1824+
});
1825+
}
1826+
1827+
#[cfg(all(debug_assertions, target_os = "linux"))]
1828+
fn current_thread_stack_size() -> usize {
1829+
use libc::{
1830+
pthread_attr_destroy, pthread_attr_getstacksize, pthread_attr_t,
1831+
pthread_getattr_np, pthread_self,
1832+
};
1833+
let mut attr: pthread_attr_t = unsafe { core::mem::zeroed() };
1834+
unsafe {
1835+
assert_eq!(pthread_getattr_np(pthread_self(), &mut attr), 0);
1836+
let mut size = 0;
1837+
assert_eq!(pthread_attr_getstacksize(&attr, &mut size), 0);
1838+
pthread_attr_destroy(&mut attr);
1839+
size
1840+
}
1841+
}
1842+
1843+
#[cfg(all(debug_assertions, target_os = "macos"))]
1844+
fn current_thread_stack_size() -> usize {
1845+
unsafe { libc::pthread_get_stacksize_np(libc::pthread_self()) }
1846+
}
1847+
}
17931848
}

0 commit comments

Comments
 (0)