Skip to content

Abort on some large allocation requests, Panic on other #26951

Open

Description

@bluss

View all comments

Compare these two:

let v = vec![0u8; !0];
let u = vec![0u16; !0];

We request a vector with 18446744073709551615 elements.

  • For u8 we receive out of memory (null) from the allocator, and call alloc::oom, which aborts the program: application terminated abnormally with signal 4 (Illegal instruction)
  • For u16, we get an assertion: thread '<main>' panicked at 'capacity overflow', ../src/libcore/option.rs:330

This is inconsistent. Why don't we abort on both of these cases? We abort on too large allocation requests, so those that are even larger could abort too?

Activity

  1. arielb1 commented on Jul 11, 2015

    @arielb1
    Contributor

    @bluss

    For the record, your code is the equivalent of

    fn main() { let v = ::std::vec::from_elem(0u16, !0); }

    In the u16 case, the amount of memory that is supposed to be allocated is 2*(2^64 - 1), which exceeds the size of usize. That is checked for rather explicitly in Vec::with_capacity. Maybe we should abort instead of panicking in that case too.

  2. bluss commented on Jul 11, 2015

    @bluss
    ContributorAuthor

    Additional info: already !0 bytes is more than we can safely allow allocating (due to known fact #22104). We should treat that as capacity overflow, but decline to do so, because it will abort anyway.

  3. steveklabnik commented on Jul 16, 2015

    @steveklabnik
    Contributor

    /cc @rust-lang/compiler , I think?

  4. Gankra commented on Jul 16, 2015

    @Gankra
    Contributor

    This is a @rust-lang/libs problem.

    I am of the opinion that aborts should only occur on problems that unwinding can't address. That is, if you overflow the capacity we have to check for that, and we can panic right away. Everything will be cleaned up and the world will be happy.

    However if an allocation makes it past our checks and then fails in the allocator, we have historically conservatively assumed that you are legitimately OOM and abort; unwinding can lead to arbitrary other allocations which is presumably bad to do during OOM.

    In practice, the platforms we support basically can't OOM naturally (by which I mean, you're asking for the last few bits of free memory), so I reckon if you ever managed to trigger an OOM it's because you managed to request All The Memory, in which case there's plenty of slack space for unwinding. If it's a legit OOM then we'll double panic and crash anyway.

    However #14674 mentions that aborting on OOM enables LLVM to better optimize around allocations. I don't know the details.

    Edit: Regardless, I do not want to add any kind of "heuristic checks" for "will probably OOM" or "was probably a real OOM".

  5. Aatch commented on Jul 17, 2015

    @Aatch
    Contributor

    I think that, ultimately, abort vs. unwind behaviour should be left up to the implementation, but the standard library should abort on failed allocations. This particular example is just a red herring. It's only because u8 is a single byte that it doesn't trigger any kind of arithmetic overflow behaviour. It's an interesting quirk, but doesn't really reflect any real inconsistent behaviour.

  6. Gankra commented on Jul 17, 2015

    @Gankra
    Contributor

    Also to be completely clear to those not super familiar with the issue: the "odd" behaviour described in this issue is completely by design, and not a mistake. Rather this issue is positing that the behaviour should be changed to be more consistent.

  7. bluss commented on Jul 24, 2015

    @bluss
    ContributorAuthor

    Of course. I don't think it's completely by design, it's patched up to be this way. For example, if we know a capacity of > isize::MAX is always too large, why does that abort and not panic?

  8. Gankra commented on Jul 24, 2015

    @Gankra
    Contributor

    @bluss any time we detect this, I believe we panic. Only alloc(..) == null aborts to my knowledge: https://github.com/rust-lang/rust/blob/master/src/liballoc/raw_vec.rs

    We simply don't even check in some cases where we know any degeneracies will be caught by the allocator (e.g. when doubling capacity on 64-bit).

  9. Gankra commented on Jul 24, 2015

    @Gankra
    Contributor

    Note that checking for > isize::MAX on 64-bit is almost surely useless since your system will OOM you far before that point using normal growth. If you explicit ask for > isize::MAX then you're basically arbitrarily "lucky". It's almost inconsistent to check, in that regard.

  10. nagisa commented on Jul 24, 2015

    @nagisa
    Member

    checking for > isize::MAX on 64-bit is almost surely useless since your system will OOM you far before that point using normal growth.

    That’s what they said about 32 bits timestamps and addressing too.

  11. Gankra commented on Jul 24, 2015

    @Gankra
    Contributor

    @nagisa Today this is simply a hard hardware issue: you only get 48 bits of the address space.

  12. nagisa commented on Jul 24, 2015

    @nagisa
    Member

    Yes, but it is always useful to be aware of future prospects.

  13. Gankra commented on Jul 24, 2015

    @Gankra
    Contributor

    @nagisa I have thankfully architected the current design to make such an upgrade trivial. In fact, all you have to do is delete some code!

    https://github.com/rust-lang/rust/blob/master/src/liballoc/raw_vec.rs#L445-L453

  14. steveklabnik commented on Aug 4, 2015

    @steveklabnik
    Contributor

    So is this not a bug?

  15. Gankra commented on Aug 4, 2015

    @Gankra
    Contributor

    It's not an accident, at least.

    On Tue, Aug 4, 2015 at 2:54 PM, Steve Klabnik notifications@github.com
    wrote:

    So is this not a bug?

    —
    Reply to this email directly or view it on GitHub
    #26951 (comment).

  16. 27 remaining items

  17. added
    E-hardCall for participation: Hard difficulty. Experience needed to fix: A lot.
    P-lowLow priority
    and removed on Aug 4, 2016
  18. lilith commented on Aug 28, 2016

    @lilith
    Contributor

    @pythonesque Why? If it's not possible to deal with OOM in Rust, I can't use Rust. See https://internals.rust-lang.org/t/could-we-support-unwinding-from-oom-at-least-for-collections/3673/21 for an exploration of why handling OOM is important.

  19. eddyb commented on Aug 28, 2016

    @eddyb
    Contributor

    @nathanaeljones IMO the real problem here is that we don't have an allocator abstraction in libstd yet.

  20. steveklabnik commented on Aug 29, 2016

    @steveklabnik
    Contributor

    @nathanaeljones the default, std allocator aborts, but given Rust's low-level nature, you could replace it with one that doesn't. It is a lot more work right now, but as @eddyb said, this is something that will come along in time.

  21. lilith commented on Aug 29, 2016

    @lilith
    Contributor

    @steveklabnik Thanks; I'm not too concerned with 'lots of work', but rather if I can produce well-behaved binaries for both FFI and server use.

    I started porting Imageflow over to Rust in June, but failed to verify that OOM panics (as widely reported) instead of aborting. I'd assumed that the stabilization of std::panic::catch_unwind meant Rust was ready for my use case. I decided to write a test today and, well, panicked.

    It's very unclear to me where the anti-OOM-panic sentiment arises from. Double panics abort.

    I've been trying to track down blockers around this, but haven't been able to discover the reasons why movement on graceful OOM handling has been slow. I think I ?might? have found the right issue to discuss: #27700 (comment)

  22. steveklabnik commented on Dec 25, 2019

    @steveklabnik
    Contributor

    Triage: still blocked on a libs team decision.

  23. ehsanul commented on May 11, 2020

    @ehsanul
    Contributor

    Looks like there is now an unstable alloc_error_handler feature gate. Tracking issue: #51540

    Mentioned in that issue, there is also an accepted RFC to set a cargo attribute for oom=panic, tracking issue: #43596

    And another issue to make the default for handle_alloc_error panic, which has survived the FCP with disposition to merge: #66741

  24. Amanieu commented on Sep 22, 2021

    @Amanieu
    Member

    We discussed this in the Libs API meeting today: the consensus was that we should pipe "invalid layout" errors through to alloc_error_handler by making it take a enum with either AllocErr and LayoutErr. This would be similar to the existing unstable TryReserveError type, and in fact we could reuse the same type in both places.

  25. 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 allocatorsC-bugCategory: This is a bug.E-hardCall for participation: Hard difficulty. Experience needed to fix: A lot.P-lowLow priorityT-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