diff options
| author | bors <bors@rust-lang.org> | 2022-10-12 10:51:31 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-10-12 10:51:31 +0000 |
| commit | 50f6d337c632cbb5bd1dc3a80b5d428cebd8dce4 (patch) | |
| tree | c241dcdf7f0df64cbd1ecdfdf055f905182b11ac /src/test/ui | |
| parent | e6ce5627a9e8af9ae4673a390954fffaf526e5cc (diff) | |
| parent | 4c5d6bb490af0b046c4e445ae90edc0c38623591 (diff) | |
Auto merge of #102460 - flba-eb:fix_85261_prevent_alloc_after_fork, r=thomcc
Prevent UB in child process after calling libc::fork After calling libc::fork, the child process tried to access a TLS variable when processing a panic. This caused a memory allocation which is UB in the child. To prevent this from happening, the panic handler will not access the TLS variable in case `panic::always_abort` was called before. Fixes #85261 (not only on Android systems, but also on Linux/QNX with TLS disabled, see issue for more details) Main drawbacks of this fix: * Panic messages can incorrectly omit `core::panic::PanicInfo` struct in case several panics (of multiple threads) occur at the same time. The handler cannot distinguish between multiple panics in different threads or recursive ones in the same thread, but the message will contain a hint about the uncertainty. * `panic_count::increase()` will be a bit slower as it has an additional `if`, but this should be irrelevant as it is only called in case of a panic.
Diffstat (limited to 'src/test/ui')
| -rw-r--r-- | src/test/ui/process/process-panic-after-fork.rs | 43 |
1 files changed, 42 insertions, 1 deletions
diff --git a/src/test/ui/process/process-panic-after-fork.rs b/src/test/ui/process/process-panic-after-fork.rs index d0a938c03e8..6d4d2492225 100644 --- a/src/test/ui/process/process-panic-after-fork.rs +++ b/src/test/ui/process/process-panic-after-fork.rs @@ -5,7 +5,6 @@ // ignore-sgx no libc // ignore-emscripten no processes // ignore-sgx no processes -// ignore-android: FIXME(#85261) // ignore-fuchsia no fork #![feature(rustc_private)] @@ -79,7 +78,49 @@ unsafe impl<A:GlobalAlloc> GlobalAlloc for PidChecking<A> { fn expect_aborted(status: ExitStatus) { dbg!(status); let signal = status.signal().expect("expected child process to die of signal"); + + #[cfg(not(target_os = "android"))] assert!(signal == libc::SIGABRT || signal == libc::SIGILL || signal == libc::SIGTRAP); + + #[cfg(target_os = "android")] + { + // Android signals an abort() call with SIGSEGV at address 0xdeadbaad + // See e.g. https://groups.google.com/g/android-ndk/c/laW1CJc7Icc + assert!(signal == libc::SIGSEGV); + + // Additional checks performed: + // 1. Find last tombstone (similar to coredump but in text format) from the + // same executable (path) as we are (must be because of usage of fork): + // This ensures that we look into the correct tombstone. + // 2. Cause of crash is a SIGSEGV with address 0xdeadbaad. + // 3. libc::abort call is in one of top two functions on callstack. + // The last two steps distinguish between a normal SIGSEGV and one caused + // by libc::abort. + + let this_exe = std::env::current_exe().unwrap().into_os_string().into_string().unwrap(); + let exe_string = format!(">>> {this_exe} <<<"); + let tombstone = (0..100) + .map(|n| format!("/data/tombstones/tombstone_{n:02}")) + .filter(|f| std::path::Path::new(&f).exists()) + .map(|f| std::fs::read_to_string(&f).expect("Cannot read tombstone file")) + .filter(|f| f.contains(&exe_string)) + .last() + .expect("no tombstone found"); + + println!("Content of tombstone:\n{tombstone}"); + + assert!( + tombstone.contains("signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad") + ); + let abort_on_top = tombstone + .lines() + .skip_while(|l| !l.contains("backtrace:")) + .skip(1) + .take_while(|l| l.starts_with(" #")) + .take(2) + .any(|f| f.contains("/system/lib/libc.so (abort")); + assert!(abort_on_top); + } } fn main() { |
