| //@ revisions: aarch64 x32 x64 |
| //@ run-pass |
| //@[aarch64] only-aarch64 |
| //@[x32] only-x86 |
| //@[x64] only-x86_64 |
| //@ ignore-emscripten no processes |
| //@ ignore-sgx no processes |
| //@ ignore-fuchsia no exception handler registered for segfault |
| //@ ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino |
| //@ ignore-ios Stack probes are enabled, but the SIGSEGV handler isn't |
| //@ ignore-tvos Stack probes are enabled, but the SIGSEGV handler isn't |
| //@ ignore-watchos Stack probes are enabled, but the SIGSEGV handler isn't |
| //@ ignore-visionos Stack probes are enabled, but the SIGSEGV handler isn't |
| |
| use std::env; |
| use std::mem::MaybeUninit; |
| use std::process::Command; |
| use std::thread; |
| |
| #[link(name = "rust_test_helpers", kind = "static")] |
| extern "C" { |
| #[link_name = "rust_dbg_extern_identity_u64"] |
| fn black_box(u: u64); |
| } |
| |
| fn main() { |
| let args = env::args().skip(1).collect::<Vec<_>>(); |
| if args.len() > 0 { |
| match &args[0][..] { |
| "main-recurse" => overflow_recurse(), |
| "child-recurse" => thread::spawn(overflow_recurse).join().unwrap(), |
| "child-frame" => overflow_frame(), |
| _ => panic!(), |
| } |
| return; |
| } |
| |
| let me = env::current_exe().unwrap(); |
| |
| // The linux kernel has some different behavior for the main thread because |
| // the main thread's stack can typically grow. We can't always guarantee |
| // that we report stack overflow on the main thread, see #43052 for some |
| // details |
| if cfg!(not(target_os = "linux")) { |
| assert_overflow(Command::new(&me).arg("main-recurse")); |
| } |
| assert_overflow(Command::new(&me).arg("child-recurse")); |
| assert_overflow(Command::new(&me).arg("child-frame")); |
| } |
| |
| #[allow(unconditional_recursion)] |
| fn recurse(array: &MaybeUninit<[u64; 1024]>) { |
| unsafe { |
| black_box(array.as_ptr() as u64); |
| } |
| let local: MaybeUninit<[u64; 1024]> = MaybeUninit::uninit(); |
| recurse(&local); |
| } |
| |
| #[inline(never)] |
| fn overflow_recurse() { |
| recurse(&MaybeUninit::uninit()); |
| } |
| |
| fn overflow_frame() { |
| // By using a 1MiB stack frame with only 512KiB stack, we'll jump over any |
| // guard page, even with 64K pages -- but stack probes should catch it. |
| const STACK_SIZE: usize = 512 * 1024; |
| thread::Builder::new().stack_size(STACK_SIZE).spawn(|| { |
| let local: MaybeUninit<[u8; 2 * STACK_SIZE]> = MaybeUninit::uninit(); |
| unsafe { |
| black_box(local.as_ptr() as u64); |
| } |
| }).unwrap().join().unwrap(); |
| } |
| |
| fn assert_overflow(cmd: &mut Command) { |
| let output = cmd.output().unwrap(); |
| assert!(!output.status.success()); |
| let stdout = String::from_utf8_lossy(&output.stdout); |
| let stderr = String::from_utf8_lossy(&output.stderr); |
| println!("status: {}", output.status); |
| println!("stdout: {}", stdout); |
| println!("stderr: {}", stderr); |
| assert!(stdout.is_empty()); |
| assert!(stderr.contains("has overflowed its stack\n")); |
| } |