about summary refs log tree commit diff
path: root/library/alloc/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-04-22 12:27:45 +0000
committerbors <bors@rust-lang.org>2023-04-22 12:27:45 +0000
commit39cf520299794e6c1b6b471db5d9935e0c6271ab (patch)
tree73cb9a139e96bd86137ba715a490d00ea12c5b77 /library/alloc/src
parent21fab435da99d6ef14c1c870650ee976499564f3 (diff)
parent4da05e0b88d8b51fc6912da2d0b93edb2780e76b (diff)
downloadrust-39cf520299794e6c1b6b471db5d9935e0c6271ab.tar.gz
rust-39cf520299794e6c1b6b471db5d9935e0c6271ab.zip
Auto merge of #109507 - Amanieu:panic-oom-payload, r=davidtwco
Report allocation errors as panics

OOM is now reported as a panic but with a custom payload type (`AllocErrorPanicPayload`) which holds the layout that was passed to `handle_alloc_error`.

This should be review one commit at a time:
- The first commit adds `AllocErrorPanicPayload` and changes allocation errors to always be reported as panics.
- The second commit removes `#[alloc_error_handler]` and the `alloc_error_hook` API.

ACP: https://github.com/rust-lang/libs-team/issues/192

Closes #51540
Closes #51245
Diffstat (limited to 'library/alloc/src')
-rw-r--r--library/alloc/src/alloc.rs94
-rw-r--r--library/alloc/src/lib.rs1
2 files changed, 78 insertions, 17 deletions
diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs
index 6f2ba957bcd..0db23e55a86 100644
--- a/library/alloc/src/alloc.rs
+++ b/library/alloc/src/alloc.rs
@@ -14,6 +14,11 @@ use core::ptr::{self, NonNull};
 #[doc(inline)]
 pub use core::alloc::*;
 
+#[cfg(not(no_global_oom_handling))]
+use core::any::Any;
+#[cfg(not(no_global_oom_handling))]
+use core::panic::BoxMeUp;
+
 #[cfg(test)]
 mod tests;
 
@@ -343,14 +348,77 @@ pub(crate) unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: Unique<T>, alloc: A)
     }
 }
 
-// # Allocation error handler
+/// Payload passed to the panic handler when `handle_alloc_error` is called.
+#[unstable(feature = "panic_oom_payload", issue = "none")]
+#[derive(Debug)]
+pub struct AllocErrorPanicPayload {
+    layout: Layout,
+}
+
+impl AllocErrorPanicPayload {
+    /// Internal function for the standard library to clone a payload.
+    #[unstable(feature = "std_internals", issue = "none")]
+    #[doc(hidden)]
+    pub fn internal_clone(&self) -> Self {
+        AllocErrorPanicPayload { layout: self.layout }
+    }
 
+    /// Returns the [`Layout`] of the allocation attempt that caused the error.
+    #[unstable(feature = "panic_oom_payload", issue = "none")]
+    pub fn layout(&self) -> Layout {
+        self.layout
+    }
+}
+
+#[unstable(feature = "std_internals", issue = "none")]
 #[cfg(not(no_global_oom_handling))]
-extern "Rust" {
-    // This is the magic symbol to call the global alloc error handler. rustc generates
-    // it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the
-    // default implementations below (`__rdl_oom`) otherwise.
-    fn __rust_alloc_error_handler(size: usize, align: usize) -> !;
+unsafe impl BoxMeUp for AllocErrorPanicPayload {
+    fn take_box(&mut self) -> *mut (dyn Any + Send) {
+        use crate::boxed::Box;
+        Box::into_raw(Box::new(self.internal_clone()))
+    }
+
+    fn get(&mut self) -> &(dyn Any + Send) {
+        self
+    }
+}
+
+// # Allocation error handler
+
+#[cfg(all(not(no_global_oom_handling), not(test)))]
+fn rust_oom(layout: Layout) -> ! {
+    if cfg!(feature = "panic_immediate_abort") {
+        core::intrinsics::abort()
+    }
+
+    extern "Rust" {
+        // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
+        // that gets resolved to the `#[panic_handler]` function.
+        #[lang = "panic_impl"]
+        fn panic_impl(pi: &core::panic::PanicInfo<'_>) -> !;
+
+        // This symbol is emitted by rustc .
+        // Its value depends on the -Zoom={unwind,abort} compiler option.
+        static __rust_alloc_error_handler_should_panic: u8;
+    }
+
+    // Hack to work around issues with the lifetime of Arguments.
+    match format_args!("memory allocation of {} bytes failed", layout.size()) {
+        fmt => {
+            // Create a PanicInfo with a custom payload for the panic handler.
+            let can_unwind = unsafe { __rust_alloc_error_handler_should_panic != 0 };
+            let mut pi = core::panic::PanicInfo::internal_constructor(
+                Some(&fmt),
+                core::panic::Location::caller(),
+                can_unwind,
+            );
+            let payload = AllocErrorPanicPayload { layout };
+            pi.set_payload(&payload);
+
+            // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
+            unsafe { panic_impl(&pi) }
+        }
+    }
 }
 
 /// Abort on memory allocation error or failure.
@@ -358,13 +426,6 @@ extern "Rust" {
 /// Callers of memory allocation APIs wishing to abort computation
 /// in response to an allocation error are encouraged to call this function,
 /// rather than directly invoking `panic!` or similar.
-///
-/// The default behavior of this function is to print a message to standard error
-/// and abort the process.
-/// It can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`].
-///
-/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html
-/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html
 #[stable(feature = "global_alloc", since = "1.28.0")]
 #[rustc_const_unstable(feature = "const_alloc_error", issue = "92523")]
 #[cfg(all(not(no_global_oom_handling), not(test)))]
@@ -375,9 +436,7 @@ pub const fn handle_alloc_error(layout: Layout) -> ! {
     }
 
     fn rt_error(layout: Layout) -> ! {
-        unsafe {
-            __rust_alloc_error_handler(layout.size(), layout.align());
-        }
+        rust_oom(layout);
     }
 
     unsafe { core::intrinsics::const_eval_select((layout,), ct_error, rt_error) }
@@ -387,6 +446,7 @@ pub const fn handle_alloc_error(layout: Layout) -> ! {
 #[cfg(all(not(no_global_oom_handling), test))]
 pub use std::alloc::handle_alloc_error;
 
+#[cfg(bootstrap)]
 #[cfg(all(not(no_global_oom_handling), not(test)))]
 #[doc(hidden)]
 #[allow(unused_attributes)]
@@ -398,7 +458,7 @@ pub mod __alloc_error_handler {
     pub unsafe fn __rdl_oom(size: usize, _align: usize) -> ! {
         extern "Rust" {
             // This symbol is emitted by rustc next to __rust_alloc_error_handler.
-            // Its value depends on the -Zoom={panic,abort} compiler option.
+            // Its value depends on the -Zoom={unwind,abort} compiler option.
             static __rust_alloc_error_handler_should_panic: u8;
         }
 
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index a002421aeef..2c6a266e2a1 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -135,6 +135,7 @@
 #![feature(maybe_uninit_slice)]
 #![feature(maybe_uninit_uninit_array)]
 #![feature(maybe_uninit_uninit_array_transpose)]
+#![feature(panic_internals)]
 #![feature(pattern)]
 #![feature(pointer_byte_offsets)]
 #![feature(provide_any)]