5757#![ stable( feature = "alloc_module" , since = "1.28.0" ) ]
5858
5959use core:: ptr:: NonNull ;
60- use core:: sync:: atomic:: { Atomic , AtomicPtr , Ordering } ;
60+ use core:: sync:: atomic:: { AtomicBool , AtomicPtr , Ordering } ;
6161use core:: { hint, mem, ptr} ;
6262
6363#[ stable( feature = "alloc_module" , since = "1.28.0" ) ]
@@ -287,7 +287,7 @@ unsafe impl Allocator for System {
287287 }
288288}
289289
290- static HOOK : Atomic < * mut ( ) > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
290+ static HOOK : AtomicPtr < ( ) > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
291291
292292/// Registers a custom allocation error hook, replacing any that was previously registered.
293293///
@@ -344,7 +344,12 @@ pub fn take_alloc_error_hook() -> fn(Layout) {
344344 if hook. is_null ( ) { default_alloc_error_hook } else { unsafe { mem:: transmute ( hook) } }
345345}
346346
347+ #[ optimize( size) ]
347348fn default_alloc_error_hook ( layout : Layout ) {
349+ if cfg ! ( panic = "immediate-abort" ) {
350+ return ;
351+ }
352+
348353 unsafe extern "Rust" {
349354 // This symbol is emitted by rustc next to __rust_alloc_error_handler.
350355 // Its value depends on the -Zoom={panic,abort} compiler option.
@@ -354,28 +359,80 @@ fn default_alloc_error_hook(layout: Layout) {
354359
355360 if unsafe { __rust_alloc_error_handler_should_panic_v2 ( ) != 0 } {
356361 panic ! ( "memory allocation of {} bytes failed" , layout. size( ) ) ;
362+ }
363+
364+ // This is the default path taken on OOM, and the only path taken on stable with std.
365+ // Crucially, it does *not* call any user-defined code, and therefore users do not have to
366+ // worry about allocation failure causing reentrancy issues. That makes it different from
367+ // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error
368+ // handler that is called when there is no `#[alloc_error_handler]`), which triggers a
369+ // regular panic and thus can invoke a user-defined panic hook, executing arbitrary
370+ // user-defined code.
371+
372+ static PREV_ALLOC_FAILURE : AtomicBool = AtomicBool :: new ( false ) ;
373+ if PREV_ALLOC_FAILURE . swap ( true , Ordering :: Relaxed ) {
374+ // Don't try to print a backtrace if a previous alloc error happened. This likely means
375+ // there is not enough memory to print a backtrace, although it could also mean that two
376+ // threads concurrently run out of memory.
377+ rtprintpanic ! (
378+ "memory allocation of {} bytes failed\n skipping backtrace printing to avoid potential recursion\n " ,
379+ layout. size( )
380+ ) ;
381+ return ;
357382 } else {
358- // This is the default path taken on OOM, and the only path taken on stable with std.
359- // Crucially, it does *not* call any user-defined code, and therefore users do not have to
360- // worry about allocation failure causing reentrancy issues. That makes it different from
361- // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error
362- // handler that is called when there is no `#[alloc_error_handler]`), which triggers a
363- // regular panic and thus can invoke a user-defined panic hook, executing arbitrary
364- // user-defined code.
365383 rtprintpanic ! ( "memory allocation of {} bytes failed\n " , layout. size( ) ) ;
366384 }
385+
386+ let Some ( mut out) = crate :: sys:: stdio:: panic_output ( ) else {
387+ return ;
388+ } ;
389+
390+ // Use a lock to prevent mixed output in multithreading context.
391+ // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
392+ // Make sure to not take this lock until after checking PREV_ALLOC_FAILURE to avoid deadlocks
393+ // when there is too little memory to print a backtrace.
394+ let mut lock = crate :: sys:: backtrace:: lock ( ) ;
395+
396+ match crate :: panic:: get_backtrace_style ( ) {
397+ // SAFETY: we took out a lock just a second ago.
398+ Some ( crate :: panic:: BacktraceStyle :: Short ) => {
399+ drop ( lock. print ( & mut out, crate :: backtrace_rs:: PrintFmt :: Short ) )
400+ }
401+ Some ( crate :: panic:: BacktraceStyle :: Full ) => {
402+ drop ( lock. print ( & mut out, crate :: backtrace_rs:: PrintFmt :: Full ) )
403+ }
404+ Some ( crate :: panic:: BacktraceStyle :: Off ) => {
405+ use crate :: io:: Write ;
406+ let _ = writeln ! (
407+ out,
408+ "note: run with `RUST_BACKTRACE=1` environment variable to display a \
409+ backtrace"
410+ ) ;
411+ if cfg ! ( miri) {
412+ let _ = writeln ! (
413+ out,
414+ "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \
415+ for the environment variable to have an effect"
416+ ) ;
417+ }
418+ }
419+ // If backtraces aren't supported or are forced-off, do nothing.
420+ None => { }
421+ }
367422}
368423
369424#[ cfg( not( test) ) ]
370425#[ doc( hidden) ]
371426#[ alloc_error_handler]
372427#[ unstable( feature = "alloc_internals" , issue = "none" ) ]
373428pub fn rust_oom ( layout : Layout ) -> ! {
374- let hook = HOOK . load ( Ordering :: Acquire ) ;
375- let hook: fn ( Layout ) =
376- if hook. is_null ( ) { default_alloc_error_hook } else { unsafe { mem:: transmute ( hook) } } ;
377- hook ( layout) ;
378- crate :: process:: abort ( )
429+ crate :: sys:: backtrace:: __rust_end_short_backtrace ( || {
430+ let hook = HOOK . load ( Ordering :: Acquire ) ;
431+ let hook: fn ( Layout ) =
432+ if hook. is_null ( ) { default_alloc_error_hook } else { unsafe { mem:: transmute ( hook) } } ;
433+ hook ( layout) ;
434+ crate :: process:: abort ( )
435+ } )
379436}
380437
381438#[ cfg( not( test) ) ]
0 commit comments