Stream: project-ffi-unwind

Topic: `extern"C"` and unwinding


view this post on Zulip Connor Horman (Sep 22 2021 at 23:33):

I was looking through: https://github.com/rust-lang/rust/pull/86155 and one thing wasn't clear to me.
It is mentioned that unwinding through extern"C" was explicitly defined as undefined behaviour, but considering the fact you can do it with pure safe rust, I can't see how that's valid (presuming that rust is fundamentally sound).
Under presumption of soundness (which is, imo, necessary in order to reason about rust at all), the following code cannot possibly have undefined behaviour within rust itself (This was mentioned in the issue a number of times):

pub extern"C" fn foo(){
    panic!(); // This line is safe, yet has undefined behaviour?
}
pub fn bar(){
    foo();  // Calling the function is also safe
}

I believe the previous reason why adding the abort-on-unwind shim wasn't possible was because this was an actual breaking change as a result of the same reasoning. Wouldn't that argument still apply in future versions when extern"C-unwind" is stable?

view this post on Zulip BatmanAoD (Kyle Strand) (Sep 23 2021 at 00:49):

Yes, that code has undefined behavior in stable Rust today, despite not having the unsafe keyword. This is actually the primary impetus for the existence of this project group.

Abort-on-unwind is a breaking change, yes, but as you've observed, it only "breaks" code that already had undefined behavior. Unfortunately, as is often the case with undefined behavior, the code generally "worked" in the sense of doing what programmers would expect, i.e., unwinding without aborting; and this behavior was relied upon in a few projects. Hence the decision to delay implementing abort-on-unwind.

The difference with C-unwindthat permits us to finally enable the abort shim is that anyone relying on the old behavior now has a well-defined way to preserve the expected behavior, i.e. by using the new ABI string where appropriate.

view this post on Zulip Connor Horman (Sep 23 2021 at 01:57):

To be honest, it's rather suprising that any safe code can have this kind of fundamental undefined behaviour - that is, undefined as a matter of the language, and not as a result of a bug in the implementation. Rust purports to rule this out entirely: https://doc.rust-lang.org/stable/nomicon/safe-unsafe-meaning.html states that "No matter what, Safe Rust can't cause Undefined Behavior.".

I have written code that I have presumed as sound, that is not under this rule, by reason of it being safe - it cannot be possible that safe operations, with all invariants upheld by adjacent unsafe code, has undefined behaviour; this is a core principle of rust. The sole exception has been code using link-control attributes, which are fundamentally unsafe.

Wrt. to using the yet-to-be-stable C-unwind abi, this has issues in any crate that has an MSRV for some reason or another (the code I mentioned above is currently MSRV 1.39, so it can be built directly with mrustc for bootstrapping purposes). It also affects any crate with the C abi in it's public api (which also affects the aforementioned code), where changing to C-unwind would be a breaking change.

view this post on Zulip Jubilee (Sep 23 2021 at 02:12):

If the Rust compiler has a soundness hole, the MSRV on that crate does not have a bearing on whether the soundness hole will be fixed, nor whether the soundness hole is fixed in a backwards-compatible way. Rather, the impact on affected code is to be minimized, not eliminated entirely:
https://rust-lang.github.io/rfcs/1122-language-semver.html

view this post on Zulip Connor Horman (Sep 23 2021 at 02:16):

I'm not referring to a soundness hole in the implementation - if it were merely so, then the fix would be exactly the above PR. As far as I can tell, this is purported to be explicit undefined behaviour in the core language, which is fundamentally unsound - that is, unsound because it is undefined behaviour, not because of some issue in the compiler.

view this post on Zulip Jubilee (Sep 23 2021 at 02:17):

If the Rust compiler implements a language that is unsound, then it is also a bug in the compiler.

view this post on Zulip Jubilee (Sep 23 2021 at 02:17):

The text you are citing, the Rustonomicon, is instructive, not definitional. i.e. it is allowed to be wrong. And in the pedagogic mode it is speaking in, the directive is as much a command as an explanation: Safe Rust cannot cause UB, ergo, you, dear reader who is likely a Rust programmer who may be wrapping unsafe APIs in safe function calls, you must prevent UB from leaking from that function.

view this post on Zulip Jubilee (Sep 23 2021 at 02:18):

That is:
Safe Rust does cause UB all the time.
And that is an issue that must be fixed however necessary.

view this post on Zulip Connor Horman (Sep 23 2021 at 02:21):

I still do not think that it is unreasonable for a programmer to assume that code that is entirely safe, or safe code mixed with unsafe, where the unsafe code is unrelated to the cause of undefined behaviour as it neither directly causes it, nor violates an invariant that results in it, cannot possibly cause any undefined behaviour in the core language of rust.

view this post on Zulip Jubilee (Sep 23 2021 at 02:24):

It is not unreasonable, and yet, that is precisely why it must be changed, because it is in fact UB.

view this post on Zulip Connor Horman (Sep 23 2021 at 02:24):

As an example of the latter, if I have a raw pointer to some type T, possibly generic, possibly a trait object. I use unsafe code to dereference it and borrow into a reference, then use that resulting reference to call a safe trait method, that code is presumed to be sound iff it can be demonstraighted that the pointer meets the requirements for dereferencing, and is entirely unrelated to the safe function.

view this post on Zulip Connor Horman (Sep 23 2021 at 02:25):

Jubilee said:

It is not unreasonable, and yet, that is precisely why it must be changed, because it is in fact UB.

Wouldn't a less destructive approach to fixing it simply to declare it defined behaviour?

view this post on Zulip Jubilee (Sep 23 2021 at 02:25):

...No?

view this post on Zulip Connor Horman (Sep 23 2021 at 02:25):

(Rather than altering the behaviour from the presumptive one and breaking code relying on it being sound)

view this post on Zulip Jubilee (Sep 23 2021 at 02:27):

I mean, I am willing to hear an argument re: how unwinding into arbitrary C code is something we can call "well-defined".

view this post on Zulip Connor Horman (Sep 23 2021 at 02:27):

(Note that this is suggested separate from changing whether rust panics can validly unwind into foreign code, or wether foreign exceptions can unwind into rust code, neither of which are possible in safe rust nor breaks locally-correct soundness reasoning of unsafe code)

view this post on Zulip Jubilee (Sep 23 2021 at 02:39):

I am pretty sure we cannot treat them as different because we cannot truly know, once the symbol is exposed to #[no_mangle] pub extern "C", that it is not currently within C, which is why it must be UB to unwind: either it is caught at the boundary or it is not.

view this post on Zulip Thom Chiovoloni (Sep 23 2021 at 02:43):

right, but this is UB without even using no_mangle, which was already implicitly unsafe

view this post on Zulip Jubilee (Sep 23 2021 at 02:43):

Indeed.

view this post on Zulip Jubilee (Sep 23 2021 at 02:49):

I guess my main remark on that is just that Rust is not the only language that does symbol mangling yet interfaces with the C ABI, however. The #[no_mangle] annotation is merely a polite request to make things easy.

view this post on Zulip BatmanAoD (Kyle Strand) (Sep 23 2021 at 02:57):

Yes, this was discussed extensively prior to the formation of the project group and leading up to the authorship of RFC-2945 (the specification of "C-unwind". I can provide links to the relevant conversations if you'd like, but we are not likely to make extern "C" unwind "soundly" rather than aborting.

view this post on Zulip BatmanAoD (Kyle Strand) (Sep 23 2021 at 03:01):

And, yes, everyone agrees that unsoundness without unsafe is bad! I recommend thinking of this as a "bug" in the Rust specification itself.

view this post on Zulip BatmanAoD (Kyle Strand) (Sep 23 2021 at 17:22):

There's some explanation & links here about the decision to move forward with introducing extern "C-unwind" and making extern "C" abort.

view this post on Zulip BatmanAoD (Kyle Strand) (Sep 23 2021 at 17:27):

https://github.com/rust-lang/project-ffi-unwind/blob/master/periodic-summaries/October-2ndHalf.md#letting-extern-c-unwind

view this post on Zulip Connor Horman (Dec 31 2021 at 01:58):

WRT. this, I actually have an issue filed (effectively a tracking issue) in lccc (host). I do hope that it is possible to fix soon.
https://github.com/LightningCreations/lccc/issues/6

view this post on Zulip BatmanAoD (Kyle Strand) (Jan 01 2022 at 02:19):

@Connor Horman Thank you for doing that.


Last updated: Jan 26 2022 at 07:20 UTC