diff --git a/src/libcompiler_builtins b/src/libcompiler_builtins index 238647af806..e9b258bc0cd 160000 --- a/src/libcompiler_builtins +++ b/src/libcompiler_builtins @@ -1 +1 @@ -Subproject commit 238647af806470dc73e585c03682083931d29cd5 +Subproject commit e9b258bc0cde0f7ed4e36ca96a25d7575e1e58b0 diff --git a/src/librustc_back/target/i386_apple_ios.rs b/src/librustc_back/target/i386_apple_ios.rs index a6383179f3a..0e4e6900024 100644 --- a/src/librustc_back/target/i386_apple_ios.rs +++ b/src/librustc_back/target/i386_apple_ios.rs @@ -26,6 +26,7 @@ pub fn target() -> TargetResult { linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { max_atomic_width: Some(64), + stack_probes: true, .. base } }) diff --git a/src/librustc_back/target/i686_apple_darwin.rs b/src/librustc_back/target/i686_apple_darwin.rs index 6b14972e9f7..8c931f18411 100644 --- a/src/librustc_back/target/i686_apple_darwin.rs +++ b/src/librustc_back/target/i686_apple_darwin.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "yonah".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); + base.stack_probes = true; Ok(Target { llvm_target: "i686-apple-darwin".to_string(), diff --git a/src/librustc_back/target/i686_linux_android.rs b/src/librustc_back/target/i686_linux_android.rs index a5390cbfb72..565fbe37bf8 100644 --- a/src/librustc_back/target/i686_linux_android.rs +++ b/src/librustc_back/target/i686_linux_android.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { // http://developer.android.com/ndk/guides/abis.html#x86 base.cpu = "pentiumpro".to_string(); base.features = "+mmx,+sse,+sse2,+sse3,+ssse3".to_string(); + base.stack_probes = true; Ok(Target { llvm_target: "i686-linux-android".to_string(), diff --git a/src/librustc_back/target/i686_unknown_dragonfly.rs b/src/librustc_back/target/i686_unknown_dragonfly.rs index 052bc23c119..9eda49a3709 100644 --- a/src/librustc_back/target/i686_unknown_dragonfly.rs +++ b/src/librustc_back/target/i686_unknown_dragonfly.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "i686-unknown-dragonfly".to_string(), diff --git a/src/librustc_back/target/i686_unknown_freebsd.rs b/src/librustc_back/target/i686_unknown_freebsd.rs index d77a9cca268..041f3070c95 100644 --- a/src/librustc_back/target/i686_unknown_freebsd.rs +++ b/src/librustc_back/target/i686_unknown_freebsd.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "i686-unknown-freebsd".to_string(), diff --git a/src/librustc_back/target/i686_unknown_haiku.rs b/src/librustc_back/target/i686_unknown_haiku.rs index b0e67bd90dd..f21c2f8c77a 100644 --- a/src/librustc_back/target/i686_unknown_haiku.rs +++ b/src/librustc_back/target/i686_unknown_haiku.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); + base.stack_probes = true; Ok(Target { llvm_target: "i686-unknown-haiku".to_string(), diff --git a/src/librustc_back/target/i686_unknown_linux_gnu.rs b/src/librustc_back/target/i686_unknown_linux_gnu.rs index 3c5c1067626..f7b916816b3 100644 --- a/src/librustc_back/target/i686_unknown_linux_gnu.rs +++ b/src/librustc_back/target/i686_unknown_linux_gnu.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "i686-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/i686_unknown_linux_musl.rs b/src/librustc_back/target/i686_unknown_linux_musl.rs index 3ed8c94d0bf..00567d70fd6 100644 --- a/src/librustc_back/target/i686_unknown_linux_musl.rs +++ b/src/librustc_back/target/i686_unknown_linux_musl.rs @@ -17,6 +17,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,-melf_i386".to_string()); + base.stack_probes = true; // The unwinder used by i686-unknown-linux-musl, the LLVM libunwind // implementation, apparently relies on frame pointers existing... somehow. diff --git a/src/librustc_back/target/i686_unknown_netbsd.rs b/src/librustc_back/target/i686_unknown_netbsd.rs index fc92e5aee6a..7a9de529566 100644 --- a/src/librustc_back/target/i686_unknown_netbsd.rs +++ b/src/librustc_back/target/i686_unknown_netbsd.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "i686-unknown-netbsdelf".to_string(), diff --git a/src/librustc_back/target/i686_unknown_openbsd.rs b/src/librustc_back/target/i686_unknown_openbsd.rs index 7ef68bd6d9c..b19bdbe049b 100644 --- a/src/librustc_back/target/i686_unknown_openbsd.rs +++ b/src/librustc_back/target/i686_unknown_openbsd.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "i686-unknown-openbsd".to_string(), diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 983a98e350c..2cc10533216 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -409,6 +409,9 @@ pub struct TargetOptions { /// Whether or not the CRT is statically linked by default. pub crt_static_default: bool, + + /// Whether or not stack probes (__rust_probestack) are enabled + pub stack_probes: bool, } impl Default for TargetOptions { @@ -466,6 +469,7 @@ impl Default for TargetOptions { panic_strategy: PanicStrategy::Unwind, abi_blacklist: vec![], crt_static_default: false, + stack_probes: false, } } } @@ -688,6 +692,7 @@ impl Target { key!(min_atomic_width, Option); try!(key!(panic_strategy, PanicStrategy)); key!(crt_static_default, bool); + key!(stack_probes, bool); if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) { for name in array.iter().filter_map(|abi| abi.as_string()) { @@ -874,6 +879,7 @@ impl ToJson for Target { target_option_val!(max_atomic_width); target_option_val!(panic_strategy); target_option_val!(crt_static_default); + target_option_val!(stack_probes); if default.abi_blacklist != self.options.abi_blacklist { d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter() diff --git a/src/librustc_back/target/x86_64_apple_darwin.rs b/src/librustc_back/target/x86_64_apple_darwin.rs index 8fd1b80430f..8ac76679008 100644 --- a/src/librustc_back/target/x86_64_apple_darwin.rs +++ b/src/librustc_back/target/x86_64_apple_darwin.rs @@ -17,6 +17,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(128); // core2 support cmpxchg16b base.eliminate_frame_pointer = false; base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-apple-darwin".to_string(), diff --git a/src/librustc_back/target/x86_64_apple_ios.rs b/src/librustc_back/target/x86_64_apple_ios.rs index bbd81fd86ff..61a71da2162 100644 --- a/src/librustc_back/target/x86_64_apple_ios.rs +++ b/src/librustc_back/target/x86_64_apple_ios.rs @@ -26,6 +26,7 @@ pub fn target() -> TargetResult { linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { max_atomic_width: Some(64), + stack_probes: true, .. base } }) diff --git a/src/librustc_back/target/x86_64_linux_android.rs b/src/librustc_back/target/x86_64_linux_android.rs index 75cf3e12438..158e2b13604 100644 --- a/src/librustc_back/target/x86_64_linux_android.rs +++ b/src/librustc_back/target/x86_64_linux_android.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-linux-android".to_string(), diff --git a/src/librustc_back/target/x86_64_rumprun_netbsd.rs b/src/librustc_back/target/x86_64_rumprun_netbsd.rs index ec5cc197dfc..c7e5edde63d 100644 --- a/src/librustc_back/target/x86_64_rumprun_netbsd.rs +++ b/src/librustc_back/target/x86_64_rumprun_netbsd.rs @@ -25,6 +25,7 @@ pub fn target() -> TargetResult { base.disable_redzone = true; base.no_default_libraries = false; base.exe_allocation_crate = None; + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-rumprun-netbsd".to_string(), diff --git a/src/librustc_back/target/x86_64_sun_solaris.rs b/src/librustc_back/target/x86_64_sun_solaris.rs index fe8691f3695..38a38ed68bc 100644 --- a/src/librustc_back/target/x86_64_sun_solaris.rs +++ b/src/librustc_back/target/x86_64_sun_solaris.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-pc-solaris".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_bitrig.rs b/src/librustc_back/target/x86_64_unknown_bitrig.rs index 5f87fe177a9..cf4b019dce2 100644 --- a/src/librustc_back/target/x86_64_unknown_bitrig.rs +++ b/src/librustc_back/target/x86_64_unknown_bitrig.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-bitrig".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_dragonfly.rs b/src/librustc_back/target/x86_64_unknown_dragonfly.rs index 96f608409ff..8885d89c6f7 100644 --- a/src/librustc_back/target/x86_64_unknown_dragonfly.rs +++ b/src/librustc_back/target/x86_64_unknown_dragonfly.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-dragonfly".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_freebsd.rs b/src/librustc_back/target/x86_64_unknown_freebsd.rs index 500629a1680..95870f2be5f 100644 --- a/src/librustc_back/target/x86_64_unknown_freebsd.rs +++ b/src/librustc_back/target/x86_64_unknown_freebsd.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-freebsd".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_fuchsia.rs b/src/librustc_back/target/x86_64_unknown_fuchsia.rs index 6e37896d414..1aebb885595 100644 --- a/src/librustc_back/target/x86_64_unknown_fuchsia.rs +++ b/src/librustc_back/target/x86_64_unknown_fuchsia.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-fuchsia".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_haiku.rs b/src/librustc_back/target/x86_64_unknown_haiku.rs index 7fab9128b29..3794a516ec4 100644 --- a/src/librustc_back/target/x86_64_unknown_haiku.rs +++ b/src/librustc_back/target/x86_64_unknown_haiku.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-haiku".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs index f73055cebaa..d2135f8a0bd 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_linux_musl.rs b/src/librustc_back/target/x86_64_unknown_linux_musl.rs index 38b9c0bace5..7d542b4d3cb 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_musl.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_musl.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-linux-musl".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_netbsd.rs b/src/librustc_back/target/x86_64_unknown_netbsd.rs index 6fe2e3fc08e..5d49fcbd64a 100644 --- a/src/librustc_back/target/x86_64_unknown_netbsd.rs +++ b/src/librustc_back/target/x86_64_unknown_netbsd.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-netbsd".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_openbsd.rs b/src/librustc_back/target/x86_64_unknown_openbsd.rs index b292b5fc1e4..aa289fb5775 100644 --- a/src/librustc_back/target/x86_64_unknown_openbsd.rs +++ b/src/librustc_back/target/x86_64_unknown_openbsd.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-openbsd".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_redox.rs b/src/librustc_back/target/x86_64_unknown_redox.rs index a693e76099b..8d2a7afeeac 100644 --- a/src/librustc_back/target/x86_64_unknown_redox.rs +++ b/src/librustc_back/target/x86_64_unknown_redox.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; Ok(Target { llvm_target: "x86_64-unknown-redox".to_string(), diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs index fa93c005dcd..cbad43066e4 100644 --- a/src/librustc_trans/attributes.rs +++ b/src/librustc_trans/attributes.rs @@ -11,13 +11,14 @@ use std::ffi::{CStr, CString}; +use rustc::session::config::Sanitizer; + use llvm::{self, Attribute, ValueRef}; use llvm::AttributePlace::Function; pub use syntax::attr::{self, InlineAttr}; use syntax::ast; use context::CrateContext; - /// Mark LLVM function to use provided inline heuristic. #[inline] pub fn inline(val: ValueRef, inline: InlineAttr) { @@ -69,6 +70,28 @@ pub fn set_frame_pointer_elimination(ccx: &CrateContext, llfn: ValueRef) { } } +pub fn set_probestack(ccx: &CrateContext, llfn: ValueRef) { + // Only use stack probes if the target specification indicates that we + // should be using stack probes + if !ccx.sess().target.target.options.stack_probes { + return + } + + // Currently stack probes seem somewhat incompatible with the address + // sanitizer. With asan we're already protected from stack overflow anyway + // so we don't really need stack probes regardless. + match ccx.sess().opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) => return, + _ => {} + } + + // Flag our internal `__rust_probestack` function as the stack probe symbol. + // This is defined in the `compiler-builtins` crate for each architecture. + llvm::AddFunctionAttrStringValue( + llfn, llvm::AttributePlace::Function, + cstr("probe-stack\0"), cstr("__rust_probestack\0")); +} + /// Composite function which sets LLVM attributes for function depending on its AST (#[attribute]) /// attributes. pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) { @@ -76,6 +99,7 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs)); set_frame_pointer_elimination(ccx, llfn); + set_probestack(ccx, llfn); let mut target_features = vec![]; for attr in attrs { if attr.check_name("target_feature") { diff --git a/src/llvm b/src/llvm index 8e1b4fedfa4..f0a23af57a1 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit 8e1b4fedfa4542e65f82fe124bd6433f3bd0aec5 +Subproject commit f0a23af57a17658e75d6dc50568397590a0e6664 diff --git a/src/test/codegen/stack-probes.rs b/src/test/codegen/stack-probes.rs new file mode 100644 index 00000000000..b7c794f4475 --- /dev/null +++ b/src/test/codegen/stack-probes.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-arm +// ignore-wasm +// ignore-emscripten +// ignore-windows +// no-system-llvm +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] + +#[no_mangle] +pub fn foo() { +// CHECK: @foo() unnamed_addr #0 +// CHECK: attributes #0 = { {{.*}}"probe-stack"="__rust_probestack"{{.*}} } +} diff --git a/src/test/run-pass/stack-probes-lto.rs b/src/test/run-pass/stack-probes-lto.rs new file mode 100644 index 00000000000..045ba408137 --- /dev/null +++ b/src/test/run-pass/stack-probes-lto.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-arm +// ignore-wasm +// ignore-emscripten +// ignore-musl FIXME #31506 +// ignore-pretty +// no-system-llvm +// compile-flags: -C lto +// no-prefer-dynamic + +include!("stack-probes.rs"); diff --git a/src/test/run-pass/stack-probes.rs b/src/test/run-pass/stack-probes.rs new file mode 100644 index 00000000000..36aacea937a --- /dev/null +++ b/src/test/run-pass/stack-probes.rs @@ -0,0 +1,68 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-arm +// ignore-wasm +// ignore-emscripten +// ignore-musl FIXME #31506 +// no-system-llvm + +use std::mem; +use std::process::Command; +use std::thread; +use std::env; + +#[link(name = "rust_test_helpers", kind = "static")] +extern { + #[link_name = "rust_dbg_extern_identity_u64"] + fn black_box(u: u64); +} + +fn main() { + let args = env::args().skip(1).collect::>(); + if args.len() > 0 { + match &args[0][..] { + "main-thread" => recurse(&[]), + "child-thread" => thread::spawn(|| recurse(&[])).join().unwrap(), + _ => 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-thread")); + } + assert_overflow(Command::new(&me).arg("child-thread")); +} + +#[allow(unconditional_recursion)] +fn recurse(array: &[u64]) { + unsafe { black_box(array.as_ptr() as u64); } + let local: [_; 1024] = unsafe { mem::uninitialized() }; + recurse(&local); +} + +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")); +}