about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorFlorian Bartels <Florian.Bartels@elektrobit.com>2022-09-29 11:04:25 +0200
committerFlorian Bartels <Florian.Bartels@elektrobit.com>2022-10-06 09:02:22 +0200
commit8e70c82f572be26a9d838e52f451b270160ffdba (patch)
treeb4e53c247716a05dbdf83f7249feabca49d548e4 /library/std/src
parent0152393048c4eb6c6d2aec63e9899cc86a269b44 (diff)
downloadrust-8e70c82f572be26a9d838e52f451b270160ffdba.tar.gz
rust-8e70c82f572be26a9d838e52f451b270160ffdba.zip
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.
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/panicking.rs27
1 files changed, 23 insertions, 4 deletions
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 4b07b393a2f..cd4d41fe123 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -308,6 +308,14 @@ pub mod panic_count {
     // Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG)
     // records whether panic::always_abort() has been called.  This can only be
     // set, never cleared.
+    // panic::always_abort() is usually called to prevent memory allocations done by
+    // the panic handling in the child created by `libc::fork`. 
+    // Memory allocations performed in  a child created with `libc::fork` are undefined
+    // behavior in most operating systems.
+    // Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory 
+    // allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is
+    // sufficient because a child process will always have exactly one thread only.
+    // See also #85261 for details.
     //
     // This could be viewed as a struct containing a single bit and an n-1-bit
     // value, but if we wrote it like that it would be more than a single word,
@@ -318,15 +326,26 @@ pub mod panic_count {
     // panicking thread consumes at least 2 bytes of address space.
     static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
 
+    // Return the state of the ALWAYS_ABORT_FLAG and number of panics.
+    //
+    // If ALWAYS_ABORT_FLAG is not set, the number is determined on a per-thread
+    // base (stored in LOCAL_PANIC_COUNT), i.e. it is the amount of recursive calls
+    // of the calling thread.
+    // If ALWAYS_ABORT_FLAG is set, the number equals the *global* number of panic
+    // calls. See above why LOCAL_PANIC_COUNT is not used.
     pub fn increase() -> (bool, usize) {
-        (
-            GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed) & ALWAYS_ABORT_FLAG != 0,
+        let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
+        let must_abort = global_count & ALWAYS_ABORT_FLAG != 0;
+        let panics = if must_abort {
+            global_count & !ALWAYS_ABORT_FLAG
+        } else {
             LOCAL_PANIC_COUNT.with(|c| {
                 let next = c.get() + 1;
                 c.set(next);
                 next
-            }),
-        )
+            })
+        };
+        (must_abort, panics)
     }
 
     pub fn decrease() {