Skip to content

Commit bc14cf2

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#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.
2 parents 514e77b + aa5cadf commit bc14cf2

File tree

2 files changed

+21
-5
lines changed

2 files changed

+21
-5
lines changed

library/std/src/panicking.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,10 @@ pub mod panic_count {
308308
// Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG)
309309
// records whether panic::always_abort() has been called. This can only be
310310
// set, never cleared.
311+
// After calling libc::fork, in the child process GLOBAL_ALWAYS_ABORT_FLAG
312+
// shall be set to prevent memory allocations and prevent access to
313+
// LOCAL_PANIC_COUNT (which can cause a memory allocation). Otherwise, undefined
314+
// behavior can occur. See also #85261 for details.
311315
//
312316
// This could be viewed as a struct containing a single bit and an n-1-bit
313317
// value, but if we wrote it like that it would be more than a single word,
@@ -318,15 +322,28 @@ pub mod panic_count {
318322
// panicking thread consumes at least 2 bytes of address space.
319323
static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
320324

325+
// Return the state of the ALWAYS_ABORT_FLAG and number of panics.
326+
//
327+
// If ALWAYS_ABORT_FLAG is not set, the number is determined on a per-thread
328+
// base (stored in LOCAL_PANIC_COUNT), i.e. it is the amount of recursive calls
329+
// of the calling thread.
330+
// If ALWAYS_ABORT_FLAG is set, the number equals the *global* number of panic
331+
// calls. In case the process was created using fork, this equals the amount
332+
// of recursive calls because a child process created by fork always has exactly
333+
// on thread.
321334
pub fn increase() -> (bool, usize) {
322-
(
323-
GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed) & ALWAYS_ABORT_FLAG != 0,
335+
let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
336+
let must_abort = global_count & ALWAYS_ABORT_FLAG != 0;
337+
let panics = if must_abort {
338+
global_count & !ALWAYS_ABORT_FLAG
339+
} else {
324340
LOCAL_PANIC_COUNT.with(|c| {
325341
let next = c.get() + 1;
326342
c.set(next);
327343
next
328-
}),
329-
)
344+
})
345+
};
346+
(must_abort, panics)
330347
}
331348

332349
pub fn decrease() {

src/test/ui/process/process-panic-after-fork.rs

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// ignore-sgx no libc
66
// ignore-emscripten no processes
77
// ignore-sgx no processes
8-
// ignore-android: FIXME(#85261)
98

109
#![feature(rustc_private)]
1110
#![feature(never_type)]

0 commit comments

Comments
 (0)