Skip to content
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

"C-unwind" ABI is unsound with "-Cpanic=abort" #83116

Closed
RalfJung opened this issue Mar 14, 2021 · 17 comments
Closed

"C-unwind" ABI is unsound with "-Cpanic=abort" #83116

RalfJung opened this issue Mar 14, 2021 · 17 comments
Labels
C-bug Category: This is a bug. F-c_unwind `#![feature(c_unwind)]` I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-medium Medium priority requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@RalfJung
Copy link
Member

RalfJung commented Mar 14, 2021

In the following code, test should explicitly be allowed to unwind:

#![feature(c_unwind)]

extern "C-unwind" {
    fn test();
}

fn main() {
    unsafe { test(); }
}

However, when building this with -Cpanic=abort, we generate LLVM IR as follows:

; Function Attrs: nounwind nonlazybind
declare void @test() unnamed_addr #1

This means unwinding of test is UB, i.e., this is a soundness problem.

This most likely also affects #[unwind(allowed)], but that attribute is slated for removal anyway.

@RalfJung RalfJung added I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness F-c_unwind `#![feature(c_unwind)]` labels Mar 14, 2021
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Mar 14, 2021
@RalfJung RalfJung added the requires-nightly This issue requires a nightly compiler in some way. label Mar 14, 2021
@JohnTitor JohnTitor added C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 14, 2021
@apiraino
Copy link
Contributor

Assigning P-medium since still a nightly-only feature. Discussed as part of the Prioritization Working Group procedure and removing I-prioritize.

@rustbot label -I-prioritize +P-medium

@rustbot rustbot added P-medium Medium priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Mar 17, 2021
@cratelyn
Copy link

I'll look into a fix for this, as the author of #76570, which introduced this issue. Thank you for flagging this @RalfJung.

@nagisa
Copy link
Member

nagisa commented Mar 28, 2021

This is probably pretty hard to fix in a way that affects least amount of code negatively.

Any function that may at any point, directly or indirectly, end up calling a C-unwind function must not be annotated with a nounwind. It can't be known in general case that this is true, this knowledge could only be available at binary load time.

So AFAICT the only viable fixes here are:

  • just don't annotate anything with nounwind, ever, -Cpanic=abort or not (we can still replace invokes with calls though);
  • annotate leaf functions (functions that don't call anything else) as nounwind and then any functions that only call other nounwind functions as nounwind.

The latter is something LLVM is already capable of inferring by itself, so there's probably little value in doing it in rustc. Maybe later, once or if MIR optimisations start caring about this, I guess?

@RalfJung
Copy link
Member Author

RalfJung commented Mar 28, 2021

There's another possible fix: wrap imported extern "C-unwind" functions in a wrapper that turns unwinding into aborts. Rust code calling the function all goes through that wrapper. Then the nounwind is actually correct.

@nagisa
Copy link
Member

nagisa commented Mar 28, 2021

We cannot do that, I think. Rust has no idea what the correct personality function would be to "catch" the unwinds from the other side of FFI. Also, isn't the intent that in situations where "C calls Rust calls C" unwinding across POF Rust frames is a supported use-case?

@bjorn3
Copy link
Member

bjorn3 commented Mar 28, 2021

There is no "correct" or "wrong" personality function to catch unwinds. Any personality function is allowed to catch any non-forced unwind. The panic_abort crate could define a personality function that always aborts when not generating a backtrace. If the wrapper function or any function directly calling "C-unwind" without wrapper doesn't use nounwind, any attempt at unwinding through rust code would abort.

@RalfJung
Copy link
Member Author

RalfJung commented Mar 28, 2021

Also, isn't the intent that in situations where "C calls Rust calls C" unwinding across POF Rust frames is a supported use-case?

I don't think this is a support use-case with -Cpanic=abort. At least, the fact that nounwind is emitted aggressively under -Cpanic=abort kind of implies that this is not a support use-case.

@nagisa
Copy link
Member

nagisa commented Mar 28, 2021

Any personality function is allowed to catch any non-forced unwind.

It is allowed to, but we don't necessarily want to expend the effort necessary to implement the personality function in a way that would allow catching e.g. all sorts of different unwinds that arbitrary runtime on the other end could generate. But I guess you're right in that we could just abort inside of the personality function itself.

I don't think this is a support use-case *with -Cpanic=abort".

Okay, I think that's a fair limitation to have. All of the binding crates that rely on "C-unwind" for their functionality would then need to always build with -Cpanic=unwind, which right now cannot be done stably or straightforwardly in Cargo.

@RalfJung
Copy link
Member Author

All of the binding crates that rely on "C-unwind" for their functionality would then need to always build with -Cpanic=unwind, which right now cannot be done stably or straightforwardly in Cargo.

Yeah, for now they'd have to tell users "you must build your final binary with -Cpanic=unwind".
Rust in principle supports linking -Cpanic=unwind rlibs into a -Cpanic=abort crate tree (that's what we do for the standard library).

@BatmanAoD
Copy link
Member

There's a fair amount of back-and-forth in #86155 between @RalfJung and @alexcrichton about whether or not this issue was resolved there. It looks to me like it was, and we can now close this (though it did not fully resolve the issue of mixed panic modes between crates). Do either of you disagree?

@alexcrichton
Copy link
Member

That sounds accurate to me, the issue here as-is in the OP at least is fixed.

@RalfJung
Copy link
Member Author

RalfJung commented May 7, 2022

Is there an issue tracking the mixed panic mode problem? That certainly needs to be tracked somewhere.

@BatmanAoD
Copy link
Member

BatmanAoD commented May 7, 2022 via email

@RalfJung
Copy link
Member Author

RalfJung commented May 7, 2022

For stabilization I think we're fairly set on just making it
(documented) UB

Oh, I didn't know that. We don't have a lot of "language primitive" UB so we should be careful here. I don't have a strong opinion on this particular one, but I think we should ensure that

Also, isn't this a soundness bug? This can be all in safe code...

@BatmanAoD
Copy link
Member

I definitely intend to add it to the undefined behavior section of the reference, as part of the documentation needed for stabilization.

And yes, it's a soundness bug, but effectively a very narrow case of an existing soundness bug that is otherwise fixed by the c-unwind feature, i.e., that it's UB to unwind at all from an extern <not Rust> function. To be fair, you could consider it new unsoundness since the "C-unwind" ABI is new, but at least that is opt-in, and the extern "C" soundness hole should be closed. Part of the rationale for not solving this remaining edge case prior to stabilization is that it's impossible to mix-and-match panic modes in Cargo, so users need to explicitly combine binaries that disagree on what panic means in order to actually have UB.

Unfortunately, I have no idea what would be involved in adding support for MIRI to detect this issue.

@RalfJung
Copy link
Member Author

Having an issue to track this might be a good start.

@BatmanAoD
Copy link
Member

I've opened a new issue here: #96926

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. F-c_unwind `#![feature(c_unwind)]` I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-medium Medium priority requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants