Skip to content

Tracking issue for the OOM hook (alloc_error_hook) #51245

Description

@glandium

View all comments

PR #50880 added an API to override the std OOM handler, similarly to the panic hook. This was discussed previously in issue #49668, after PR #50144 moved OOM handling out of the Alloc/GlobalAlloc traits. The API is somewhat similar to what existed before PR #42727 removed it without an explanation. This issue tracks the stabilization of this API.

Defined in the std::alloc module:

pub fn set_oom_hook(hook: fn(Layout) -> !);
pub fn take_oom_hook() -> fn(Layout) -> !;
pub fn set_alloc_error_hook(hook: fn(Layout));
pub fn take_alloc_error_hook() -> fn(Layout);

CC @rust-lang/libs, @SimonSapin

Unresolved questions

Activity

  1. added
    A-allocatorsArea: Custom and system allocators
    T-libs-api[DEPRECATED; DO NOT USE]
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    on May 31, 2018
  2. SimonSapin commented on May 31, 2018

    @SimonSapin
    Contributor

    We have an accepted RFC #48043 that includes adding a way to opt into of panicking instead of aborting on OOM (so that catch_unwind could be used to recover from OOM at coarse granularity). Perhaps this could be the mechanism for that?

  3. glandium commented on May 31, 2018

    @glandium
    ContributorAuthor

    Oh right, I had forgotten about that. And in fact, it works:

    #![feature(allocator_api)]
    
    use std::alloc::set_oom_hook;
    use std::alloc::Layout;
    
    fn oom(layout: Layout) -> ! {
        panic!("oom {}", layout.size());
    }
    
    fn main() {
        set_oom_hook(oom);
        let result = std::panic::catch_unwind(|| {
            let v = Vec::<u8>::with_capacity(100000000000000);
            println!("{:p}", &v[..]);
        });
        println!("{:?}", result);
    }

    cargo run with last nightly:

    thread 'main' panicked at 'oom 100000000000000', src/main.rs:7:5
    note: Run with `RUST_BACKTRACE=1` for a backtrace.
    Err(Any)
    
  4. glandium commented on May 31, 2018

    @glandium
    ContributorAuthor

    (despite the #[rustc_allocator_nounwind] annotation on alloc::alloc::oom)

  5. glandium commented on May 31, 2018

    @glandium
    ContributorAuthor

    Added item about interaction with unwinding.

  6. nikic commented on May 31, 2018

    @nikic
    Contributor

    @glandium While this example works, the #[rustc_allocator_nounwind] annotation will (likely) prevent some drops from running during unwinding (in particular those that would have to be run inside the function that performs the oom call).

  7. glandium commented on May 31, 2018

    @glandium
    ContributorAuthor

    Picture me puzzled.

    #![feature(allocator_api)]
    
    use std::alloc::set_oom_hook;
    use std::alloc::Layout;
    
    struct Foo;
    
    impl Drop for Foo {
        fn drop(&mut self) {
            println!("Foo");
        }
    }
    
    fn oom(layout: Layout) -> ! {
        let f = Foo;
        panic!("oom {}", layout.size());
    }
    
    fn main() {
        set_oom_hook(oom);
        let result = std::panic::catch_unwind(|| {
            let f = Foo;
            let v = Vec::<u8>::with_capacity(100000000000000);
            println!("{:p}", &v[..]);
        });
        println!("{:?}", result);
    }
    thread 'main' panicked at 'oom 100000000000000', src/main.rs:16:5
    note: Run with `RUST_BACKTRACE=1` for a backtrace.
    Foo
    Foo
    Err(Any)
    
  8. glandium commented on May 31, 2018

    @glandium
    ContributorAuthor

    Or

    #![feature(allocator_api)]
    
    use std::alloc::set_oom_hook;
    use std::alloc::Layout;
    
    struct Foo;
    
    impl Drop for Foo {
        fn drop(&mut self) {
            println!("Foo");
        }
    }
    
    fn oom(layout: Layout) -> ! {
        let f = Foo;
        panic!("oom {}", layout.size());
    }
    
    fn main() {
        set_oom_hook(oom);
        let result = std::panic::catch_unwind(|| {
            let f = Foo;
            std::alloc::oom(Layout::new::<u8>());
            println!("after oom");
        });
        println!("{:?}", result);
    }
    thread 'main' panicked at 'oom 1', src/main.rs:16:5
    note: Run with `RUST_BACKTRACE=1` for a backtrace.
    Foo
    Foo
    Err(Any)
    
  9. glandium commented on May 31, 2018

    @glandium
    ContributorAuthor

    Ah, I forgot --release, and with it, in both cases, the Foo instance in the closure is not dropped (the one in oom is, though).

  10. alexcrichton commented on May 31, 2018

    @alexcrichton
    Member

    With panic hooks they notably return () instead of ! which allows you to chain them (if necessary). I think if we want to use the terminology "hook" for OOM we probably want the same (not returning !) as that'll allow us to also justify take_oom_hook as otherwise I think you can't actually call that and/or delegate in an order where you don't go first?

    OOM hooks could always panic/abort themselves of course but the signature may just want to be that by default we don't require it and then the fallback of OOM is to abort as OOM does today

  11. glandium commented on May 31, 2018

    @glandium
    ContributorAuthor

    take_oom_hook is still useful albeit annoying, since hooks can't be closures. So you'd have to store the value in a global, but then you can call it from your own function. I can see how returning () would make things more flexible, though.

  12. added a commit that references this issue on Jun 1, 2018
  13. 30 remaining items

  14. added a commit that references this issue on Apr 22, 2023
    39cf520
  15. added a commit that references this issue on Apr 29, 2023
    c7155f2
  16. added a commit that references this issue on Jun 5, 2023
    6d14583
  17. added a commit that references this issue on Jun 19, 2023
    321337b
  18. SimonSapin commented on Dec 9, 2023

    @SimonSapin
    Contributor

    This tracking issue was closed when PR #109507 merged to remove the feature, but that was reverted in PR #110782. Reopening to reflect that.

    PR #112331 is open to remove the feature again, and is already marked as closing this.

  19. changed the title [-]Tracking issue for the OOM hook[/-] [+]Tracking issue for the OOM hook (`alloc_error_hook`)[/+] on Apr 17, 2024
  20. Amanieu commented on Dec 9, 2025

    @Amanieu
    Member

    We discussed this in the @rust-lang/libs-api meeting today. Considering the recent progress towards externally implementable items (#146348), we think that the best way forward would be to get rid of the OOM hooks entirely and instead just allow users to override the standard library's #[alloc_error_handler].

  21. nia-e commented on Jul 22, 2026

    @nia-e
    Member

    Per yesterday's libs-api meeting, we'd like to split set_alloc_error_hook in twain so that setters can explicitly opt-in to unwinding semantics. There have been several soundness issues resulting from unwinds on OOM, and the effective safety requirements for it are incredibly strict (auditing one's entire dependency graph, since the vast majority of unsafe code is unsound in the presence of any spurious unwinds at all), so we'd like to have a version of set_alloc_error_hook that wraps the hook in an abort-on-unwind guard. I'll send a PR implementing this, but naming is open to bikeshedding ^^

  22. added a commit that references this issue on Jul 22, 2026
  23. RalfJung commented on Jul 22, 2026

    @RalfJung
    Member

    With #159702, the docs for set_alloc_error_hook_unwinding now say

    /// Unwinding from the allocation error hook is not unsafe per se; however, large swathes of
    /// library code assume this never happens and may cause UB. Therefore, upon setting a hook
    /// that may unwind, care should be taken to audit code in dependencies for possible unsoundness
    /// in the presence of unwinds and ensure UB is not triggered.
    

    This is unprecedented and IMO plain inacceptable. No other safety comment in std or anywhere else that I have seen says "audit the entire codebase". Has anyone even given a plausible argument for how that could be practically done? I doubt it can be practically done so I think this safety comment is equivalent to saying "this is not safe, there will probably be UB later, good luck have fun". This safety-comment is unformalizable in verification tools as it does not actually have a meaning (not in the same sense that our other safety comments do). This isn't a safety comment, it's an admission of defeat.

    I am very strongly opposed to creating an ecosystem precedent for such "safety" reasoning (it doesn't deserve that name IMO). If other crates follow this example, the social contract that keeps unsafe Rust usable at scale will start eroding and risks destroying one of the key values of Rust -- an ecosystem of sound libraries that one can reason about in isolation and compose to large applications without worrying about that composition introducing UB.

  24. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-allocatorsArea: Custom and system allocatorsB-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCI-libs-radarLibs issues that are tracked on the team's radar.T-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions