Skip to content

Commit 6019f64

Browse files
committed
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.
1 parent 1536a53 commit 6019f64

File tree

1 file changed

+21
-4
lines changed

1 file changed

+21
-4
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() {

0 commit comments

Comments
 (0)