1 | use rayon_core::ThreadPoolBuilder; |
2 | |
3 | use std::env; |
4 | use std::process::{Command, ExitStatus, Stdio}; |
5 | |
6 | #[cfg (target_os = "linux" )] |
7 | use std::os::unix::process::ExitStatusExt; |
8 | |
9 | fn force_stack_overflow(depth: u32) { |
10 | let mut buffer = [0u8; 1024 * 1024]; |
11 | std::hint::black_box(&mut buffer); |
12 | if depth > 0 { |
13 | force_stack_overflow(depth - 1); |
14 | } |
15 | } |
16 | |
17 | #[cfg (unix)] |
18 | fn disable_core() { |
19 | unsafe { |
20 | libc::setrlimit( |
21 | libc::RLIMIT_CORE, |
22 | &libc::rlimit { |
23 | rlim_cur: 0, |
24 | rlim_max: 0, |
25 | }, |
26 | ); |
27 | } |
28 | } |
29 | |
30 | #[cfg (unix)] |
31 | fn overflow_code() -> Option<i32> { |
32 | None |
33 | } |
34 | |
35 | #[cfg (windows)] |
36 | fn overflow_code() -> Option<i32> { |
37 | use std::os::windows::process::ExitStatusExt; |
38 | |
39 | ExitStatus::from_raw(0xc00000fd /*STATUS_STACK_OVERFLOW*/).code() |
40 | } |
41 | |
42 | #[test] |
43 | #[cfg_attr (not(any(unix, windows)), ignore)] |
44 | fn stack_overflow_crash() { |
45 | // First check that the recursive call actually causes a stack overflow, |
46 | // and does not get optimized away. |
47 | let status = run_ignored("run_with_small_stack" ); |
48 | assert!(!status.success()); |
49 | #[cfg (any(unix, windows))] |
50 | assert_eq!(status.code(), overflow_code()); |
51 | #[cfg (target_os = "linux" )] |
52 | assert!(matches!( |
53 | status.signal(), |
54 | Some(libc::SIGABRT | libc::SIGSEGV) |
55 | )); |
56 | |
57 | // Now run with a larger stack and verify correct operation. |
58 | let status = run_ignored("run_with_large_stack" ); |
59 | assert_eq!(status.code(), Some(0)); |
60 | #[cfg (target_os = "linux" )] |
61 | assert_eq!(status.signal(), None); |
62 | } |
63 | |
64 | fn run_ignored(test: &str) -> ExitStatus { |
65 | Command::new(env::current_exe().unwrap()) |
66 | .arg("--ignored" ) |
67 | .arg("--exact" ) |
68 | .arg(test) |
69 | .stdout(Stdio::null()) |
70 | .stderr(Stdio::null()) |
71 | .status() |
72 | .unwrap() |
73 | } |
74 | |
75 | #[test] |
76 | #[ignore ] |
77 | fn run_with_small_stack() { |
78 | run_with_stack(8); |
79 | } |
80 | |
81 | #[test] |
82 | #[ignore ] |
83 | fn run_with_large_stack() { |
84 | run_with_stack(48); |
85 | } |
86 | |
87 | fn run_with_stack(stack_size_in_mb: usize) { |
88 | let pool = ThreadPoolBuilder::new() |
89 | .stack_size(stack_size_in_mb * 1024 * 1024) |
90 | .build() |
91 | .unwrap(); |
92 | pool.install(|| { |
93 | #[cfg (unix)] |
94 | disable_core(); |
95 | force_stack_overflow(32); |
96 | }); |
97 | } |
98 | |