Skip to content

Report allocation errors through the panic handler #192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Amanieu opened this issue Mar 23, 2023 · 17 comments
Open

Report allocation errors through the panic handler #192

Amanieu opened this issue Mar 23, 2023 · 17 comments
Labels
api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api

Comments

@Amanieu
Copy link
Member

Amanieu commented Mar 23, 2023

Proposal

Problem statement

We currently use a completely separate mechanism to handle allocation errors (OOM) than panics, despite both of these being very similar. However both are very similar: they involve handling runtime failures that are generally unrecoverable. It would be better to unify them under a single mechanism which is already stable: panic handlers (no_std) and panic hooks (std).

Motivation, use-cases

This change has already been done for no_std in rust-lang/rust#66741: it was noticed that the vast majority of users of #[alloc_error_handler] were just calling panic anyways. This avoided the need to stabilize the alloc_error_handler attribute by defaulting to a panic if one was not specified.

The OOM hook API has also remained unstable for a very long time with no clear path to stabilization. The best way forward would be to unify it with the panic hook API just like it has been done in no_std.

Some use cases (firefox) still need access to the size of the failed allocation, so this is provided as a custom panic payload.

Solution sketches

// alloc::alloc

pub struct AllocErrorPanicPayload { .. }

impl AllocErrorPanicPayload {
    pub fn layout(&self) -> Layout;
}

This ACP proposes to make the following changes:

In the standard library, no additional allocations are made as part of the OOM handling process, except in 1 specific situation:

  • With -Zoom=panic, if the panic hook returns then an allocation is necessary to box the payload and allocate an exception object. However it's not a big deal if this, fails, the process will simply abort immediately in that case.

Links and related work

Tracking issues that will be closed by this change:

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

@Amanieu Amanieu added T-libs-api api-change-proposal A proposal to add or alter unstable APIs in the standard libraries labels Mar 23, 2023
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Apr 17, 2023
…twco

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: rust-lang/libs-team#192

Closes rust-lang#51540
Closes rust-lang#51245
bors added a commit to rust-lang-ci/rust that referenced this issue Apr 22, 2023
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: rust-lang/libs-team#192

Closes rust-lang#51540
Closes rust-lang#51245
RalfJung pushed a commit to RalfJung/miri that referenced this issue Apr 24, 2023
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: rust-lang/libs-team#192

Closes #51540
Closes #51245
bjorn3 pushed a commit to rust-lang/rustc_codegen_cranelift that referenced this issue Apr 25, 2023
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: rust-lang/libs-team#192

Closes #51540
Closes #51245
lnicola pushed a commit to lnicola/rust-analyzer that referenced this issue May 9, 2023
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: rust-lang/libs-team#192

Closes #51540
Closes #51245
antoyo pushed a commit to rust-lang/rustc_codegen_gcc that referenced this issue Jun 3, 2023
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: rust-lang/libs-team#192

Closes #51540
Closes #51245
thomcc pushed a commit to tcdi/postgrestd that referenced this issue Jul 18, 2023
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: rust-lang/libs-team#192

Closes #51540
Closes #51245
@m-ou-se
Copy link
Member

m-ou-se commented Oct 2, 2023

I don't think this is the right thing to do.

Currently, a (no-std) #[panic_handler] never gets a payload(), not even a &'static str or String, even for something as simple as panic!("hello"). Payloads are currently a std-only feature. It is true that one can call info.payload() in a #[panic_handler], but that's likely a mistake (see rust-lang/rust#115974). That method has never done anything useful in a panic handler. Currently, any time anyone implements a #[panic_handler] by downcasting a payload() they're just adding dead code: downcasting there always returns None.

So, I disagree this proposal is a way "to unify them under a single mechanism". &dyn Any panic payloads are currently a std-only feature, that just happens to also compile (but not work) in no-std.

@Amanieu
Copy link
Member Author

Amanieu commented Oct 2, 2023

I don't see this as a strong argument to avoid using payloads, it's just that payloads are underused today. Treating OOM as a panic is almost always what people want, and allows the resulting error message to be properly integrated into whatever error reporting mechanism an application is using (e.g. uploading crash reports).

@m-ou-se
Copy link
Member

m-ou-se commented Oct 3, 2023

Using payloads in a std panic hook is fine. I'm saying that using payloads in a #[panic_handler] would be a entirely new concept. It's true that we already have a .payload() method available in #[panic_handler]s, but it doesn't do anything.

A (std) panic hook is expected to do .payload().downcast_ref<String>() (and &str) to get the panic message, but that will always return None in a #[panic_handler]. So, we can never get the concept of a 'payload' to mean the same thing in panic handlers and panic hooks. So I disagree this is a 'unified single mechanism'.

It's a valid discussion to have whether we should start exposing the concept of a panic payload in #[panic_handler], but that would be an entirely new thing, not an existing (unified) mechanism.

(And on that discussion: I don't think we should start using payloads in #[panic_handler], because it will never mean the same thing as a payload in a panic hook: it will not have a payload for formatted panic messages. And std will need to take ownership of a panic payload to be able to unwind/catch, which isn't possible when just borrowing the payload like a #[panic_handler] does.)

@m-ou-se
Copy link
Member

m-ou-se commented Oct 3, 2023

Treating OOM as a panic is almost always what people want, and allows the resulting error message to be properly integrated into whatever error reporting mechanism an application is using (e.g. uploading crash reports).

That will happen regardless of whether we have a AllocErrorPanicPayload payload mechanism or a default alloc_error_handler that panics, right?

@RalfJung
Copy link
Member

Using payloads in a std panic hook is fine. I'm saying that using payloads in a #[panic_handler] would be a entirely new concept. It's true that we already have a .payload() method available in #[panic_handler]s, but it doesn't do anything.

I've been wondering before why panic hooks and panic handlers take the same type as argument. In a sense there's really two different types here that are just merged into one -- "PanicInfo with a message" and "PanicInfo with a payload".

RalfJung pushed a commit to RalfJung/rust-analyzer that referenced this issue Apr 20, 2024
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: rust-lang/libs-team#192

Closes #51540
Closes #51245
RalfJung pushed a commit to RalfJung/rust-analyzer that referenced this issue Apr 27, 2024
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: rust-lang/libs-team#192

Closes #51540
Closes #51245
@RalfJung
Copy link
Member

RalfJung commented Apr 9, 2025

My main concern with this is that panic! will invoke the user-defined panic hook, which can (in entirely safe code) try to allocate. Calling that on an allocation failure sounds problematic.

This example seems like it could be made into a soundness issue if allocation failure can panic, using a panic hook rather than an alloc error hook. More broadly, the property "the allocator can never call arbitrary user-controlled code" seems like a pretty good property to have; not having it is a huge footgun.

@m-ou-se
Copy link
Member

m-ou-se commented Apr 9, 2025

Note that as of rust-lang/rust#115974, core (panic handler) no longer has any concept of a 'panic payload'. That is now an std (panic hook) only concept.

@RalfJung
Copy link
Member

RalfJung commented Apr 9, 2025

Yeah, that resolves the concern you raised above, I assume. Or at least the concern would have to be phrased differently now I guess?

It doesn't resolve my concern, though.

@m-ou-se
Copy link
Member

m-ou-se commented Apr 9, 2025

It "resolves" all concerns because it means this acp is no longer possible.

@RalfJung
Copy link
Member

RalfJung commented Apr 9, 2025

Custom panic payloads are still a thing though, aren't they?

@m-ou-se
Copy link
Member

m-ou-se commented Apr 10, 2025

Only in std. std::panic::panic_any will directly call the hook you set with std::panic::set_hook with any payload, but that doesn't go through any of (core's) panic machinery.

@RalfJung
Copy link
Member

Ah I see, and this here is meant to work for alloc even without std.

So... is there still a plan for what to do with alloc error handlers?

@m-ou-se
Copy link
Member

m-ou-se commented Apr 10, 2025

I think Externally Implementable Items (EII) are the solution.

@jdonszelmann and I are close to getting that implemented. (There's a working implementation that only needs some debugging and cleaning up.)

It will allow alloc to do:

#[externally_implementable]
fn alloc_error_handler(_: Layout) -> ! {
    panic!("default message");
}

And then a user can override it with something like:

#[alloc::alloc_error_handler]
fn my_alloc_error_handler(layout: Layout) -> ! {
    // ...
}

@m-ou-se
Copy link
Member

m-ou-se commented Apr 10, 2025

If we do that, then what we do in std is an open question.

Possible answers:

  1. Do nothing in std. The user can just override the #[alloc_error_handler] if they want. Or,
  2. Implement the #[alloc_error_handler] in std (just like how it implements the #[panic_handler]), and call the panic hook from it with a special AllocErrorPanicPayload payload.

Option 2 is basically doing this ACP for std only.

One could argue that option 2 is unnecessary if option 1 already works. But it's also arguable that with std it's more consistent to have everything end up in the (dynamically/runtime settable) panic hook.

(A much more controversial option 3 would be to deprecate the concept of a (std-only runtime settable) panic hook and have everything go through EII-like panic handlers (adding #[std::panic_any_handler] if you want to specially handle non-string payloads, which would call the regular panic_handler by default).)

@RalfJung
Copy link
Member

RalfJung commented Apr 10, 2025

That sounds like it would allow safe code to run arbitrary code on allocation failure, which is a big soundness footgun as described above.

I think registering a function with alloc::alloc_error_handler should be unsafe and have a safety obligation of not calling any code that might make reentrancy assumptions about the allocator.

@m-ou-se
Copy link
Member

m-ou-se commented Apr 10, 2025

Yeah, we support unsafe EIIs. We can mark the EII as unsafe with something like:

#[externally_implementable(unsafe)]
fn alloc_error_handler(_: Layout) -> ! {
    abort();
}

And then a user needs to use an unsafe attribute to override it:

#[unsafe(alloc::alloc_error_handler)]
fn my_alloc_error_handler(layout: Layout) -> ! {
    // ...
}

@m-ou-se
Copy link
Member

m-ou-se commented Apr 10, 2025

But it sounds like option 2 isn't possible then, because set_hook is safe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api
Projects
None yet
Development

No branches or pull requests

3 participants