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

panic runtime and C-unwind documentation #1226

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

BatmanAoD
Copy link
Member

@BatmanAoD BatmanAoD commented May 29, 2022

Tracking issue: rust-lang/rust#74990

@BatmanAoD BatmanAoD marked this pull request as ready for review May 30, 2022 17:35
@BatmanAoD
Copy link
Member Author

BatmanAoD commented May 30, 2022

Hm... not sure how to fix the links to the newly-introduced page. Is there an index page I need to edit?

Edit: I think I found it

@ehuss ehuss added the S-waiting-on-stabilization Waiting for a stabilization PR to be merged in the main Rust repository label Jun 22, 2022
src/items/functions.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
src/behavior-considered-undefined.md Outdated Show resolved Hide resolved
Comment on lines 219 to 224
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind |
| -------------- | ------------ | ------------------------------------- | ----------------------- |
| `panic=unwind` | `"C-unwind"` | unwind | unwind |
| `panic=unwind` | `"C"` | abort | UB |
| `panic=abort` | `"C-unwind"` | `panic!` aborts | abort |
| `panic=abort` | `"C"` | `panic!` aborts (no unwinding occurs) | UB |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind |
| -------------- | ------------ | ------------------------------------- | ----------------------- |
| `panic=unwind` | `"C-unwind"` | unwind | unwind |
| `panic=unwind` | `"C"` | abort | UB |
| `panic=abort` | `"C-unwind"` | `panic!` aborts | abort |
| `panic=abort` | `"C"` | `panic!` aborts (no unwinding occurs) | UB |
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind |
| -------------- | ------------ | ------------------------------------- | ----------------------- |
| `panic=unwind` | `"C-unwind"` | unwind | unwind |
| `panic=unwind` | `"C"` | abort if unwinding reaches the function | UB if unwinding reaches the function |
| `panic=abort` | `"C-unwind"` | aborts immediately (no unwinding occurs) | abort if unwinding reaches the function |
| `panic=abort` | `"C"` | aborts immediately (no unwinding occurs) | UB if unwinding reaches the function |

I found this a bit confusing. I believe there are subtle differences in terms of where the aborts occur and so forth. I have tried to clarify above, but I think it may be worth further clarifying.

It may also be worth adding some (perhaps non-normative) discussion of implementation:

  • When compiling a function F with panic=unwind and extern "C", the compiler inserts unwinding guards for Rust panics that trigger an abort when unwinding reaches F.

I am also be misunderstanding what's going on. I was a bit surprised to see "UB" for unforced-foreign-unwind with C=unwind. I guess that this table is combining two scenarios:

  • what happens when you call a C++ function declared as extern "C", and it unwinds (UB, we haven't compiled any guards)
  • what happens when an extern "C" Rust function invokes some C++ function that throws (probably, in practice, an abort, but perhaps we have simplified to call it UB?)

Is that right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only UB for a foreign function declared as extern "C" to unwind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbdd0121 what happens when an extern "C" Rust function unwinds? I believe we insert an abort guard, but this table doesn't clarify that, right? Or maybe I don't understand what it's trying to convey. I'm imagining a scenario like

extern "C-unwind" fn throws();

extern "C" fn rust_fn() {
    throws(); // unwinds
}

In this case, I presume you get an abort -- and I think we guarantee that? But the way I read this table, it would be listed as UB.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm....I don't know if the panic abort guard would currently catch and abort in that case, or if it relies on the personality function to only abort on true Rust panics. I agree that the behavior in the table as-written is UB.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbdd0121 what happens when an extern "C" Rust function unwinds? I believe we insert an abort guard, but this table doesn't clarify that, right? Or maybe I don't understand what it's trying to convey. I'm imagining a scenario like

extern "C-unwind" fn throws();

extern "C" fn rust_fn() {
    throws(); // unwinds
}

In this case, I presume you get an abort -- and I think we guarantee that? But the way I read this table, it would be listed as UB.

Unwinding out from extern "C" functions (defined in either Rust or foreign language) is UB.
In the case you listed, we insert guard to prevent unwinding from actually leaving a Rust extern "C" functions, therefore the function does not unwind, so UB is prevented; in this case we never unwinds out from a extern "C" Rust functions.

If you define a extern "C-unwind" Rust function and transmute it to extern "C" and then call it, it's not UB if unwinding does not happen, and it's UB if unwinding happens.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nikomatsakis With the change to the verbiage above, explaining that the table entries are specifically describing behavior at function boundaries, do you still want to make a change here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check whether the notes I suggested to add under the table are correct.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nikomatsakis Can I resolve this comment thread now?

src/linkage.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
@BatmanAoD
Copy link
Member Author

Sorry for the delay; I think I've addressed all comments.

@BatmanAoD
Copy link
Member Author

@tmandry @nikomatsakis I'm not sure you saw my comments & changes last week, but I think this is ready for re-review.

@nbdd0121
Copy link
Contributor

Could you squash the commits?

@BatmanAoD
Copy link
Member Author

@nbdd0121 Can that be done on merge? I've heard that GitHub sometimes has trouble with PR branches that receive force-pushes.

src/linkage.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
src/panic.md Outdated Show resolved Hide resolved
src/panic.md Outdated Show resolved Hide resolved
src/panic.md Outdated Show resolved Hide resolved
src/panic.md Outdated Show resolved Hide resolved
src/panic.md Outdated Show resolved Hide resolved
src/panic.md Outdated Show resolved Hide resolved
src/panic.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
@BatmanAoD
Copy link
Member Author

I think I've resolved all open questions and concerns. Is there anything else needed from me at the moment?

@nbdd0121
Copy link
Contributor

This really needs rebasing now.

@BatmanAoD
Copy link
Member Author

@nbdd0121 Done!

@BatmanAoD
Copy link
Member Author

@tmandry two changes since your review:

  • I added the new ABIs to items/external-blocks
  • I added that catching an exception or unwind from the "wrong" language is UB

@BatmanAoD
Copy link
Member Author

@tmandry @ehuss can this be merged, since the partial stabilization was a while ago now?

src/items/external-blocks.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
src/items/functions.md Outdated Show resolved Hide resolved
@traviscross traviscross added the S-waiting-on-review Status: The marked PR is awaiting review from the PR author. label Aug 30, 2024
@traviscross
Copy link
Contributor

This looks reasonable to me, though I'm also relying on the strength of the many earlier reviews.

Last call for comments. Otherwise we'll get this merged in the coming days.

@BatmanAoD BatmanAoD force-pushed the c-unwind-documentation branch 2 times, most recently from 2c341e6 to e8fd24d Compare August 31, 2024 21:50
No crate may be linked with [the `panic=abort` runtime][panic-runtime] if it has
both of the following characteristics:

* It contains a call to an `-unwind` foreign function or function pointer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This includes "Rust" foreign function calls, right? Like a separate copy of the Rust runtime, built with the same compiler and hence ABI compatible.

In the other file you carefully define unwinding and non-unwinding ABIs; can that be reused here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm not sure what you're asking with regard to runtimes. This is talking about the ABI strings C-unwind, system-unwind, etc; yes, the code being invoked may be Rust code.

What verbiage are you suggesting be reused? I don't think listing all the ABI strings seems necessary, but I can add a link to the functions.md section explaining unwinding vs non-unwinding ABIs, with the caveat that the Rust ABI itself doesn't prevent linking crates with different panic strategies.

Copy link
Member

@RalfJung RalfJung Sep 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm saying that this needs to also include extern "Rust" fn foreign function calls, but the current wording does not seem to cover those functions. I'm not sure what is the best way to fix that, but functions.md introduces the concept of an ""unwinding ABI" and that seems to also be the right concept here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, Rust calls are specifically excluded here; fn is entirely equivalent to extern "Rust" fn. So including extern "Rust" here would effectively mean "it's never legal to link panic=abort crates against panic=unwind crates," which is clearly not true.

... possibly I am still misunderstanding the question?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. What makes "Rust" different from the other unwinding APIs?

IIRC the argument was that if we link in the abort runtime, Rust itself will never trigger unwinding, so if your program has no other "*-unwind" functions anywhere (meaning there's no foreign-language unwinding) then there can be no unwinding in general?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not guaranteed to work, but yes, in theory you can do it, and it may even work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if people do it and not making sure panic agrees on both side, then it's not any different than using extern "Rust" but doesn't make sure ABI on both side match, e.g. different target feature. Also, this was unsound forever and not anything new due to c_unwind. -Cpanic=abort will make compiler assume extern "Rust" won't panic as early as Rust 1.23.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this was unsound forever and not anything new due to c_unwind. -Cpanic=abort will make compiler assume extern "Rust" won't panic as early as Rust 1.23.

Absolutely, but now we're finally writing docs for all this so it seems odd to skip this one?

But okay, in the interest of unblocking this PR, maybe let's make this an issue then: #1642.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The text here certainly shouldn't be modified to add extern "Rust", because the ffi_unwind_calls lint (in the paragraph below) works as described and does not prevent using the extern "Rust" ABI.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be okay with adding this sentence to the beginning of the "prohibited linkage scenarios" section, but I'm not 100% confident that it is accurate:

It is never valid to link code against both [panic-runtimes][panic-runtime]; linkers will often reject attempts to do so by emitting a multiple-definitions error, but if a binary is successfully produced, any panic will result in undefined behavior.

In particular, I don't know what the right terminology is to distinguish these two types of linking:

  • the same code is "linked against" both panic runtimes, in the sense that they both provide candidate definitions for the same symbol lookup at link-time (i.e., the situation I'm saying is never allowed, and which will tend to trigger multiple-definition errors)
  • the situation @RalfJung described above: "a dylib...with -Cpanic=abort [loaded] into a Rust binary that was built with -Cpanic=unwind... The dylib statically contains a Rust runtime, the main binary statically contains another independent Rust runtime."

src/items/functions.md Outdated Show resolved Hide resolved
src/panic.md Show resolved Hide resolved
src/panic.md Show resolved Hide resolved
src/destructors.md Outdated Show resolved Hide resolved
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Sep 28, 2024
… r=Mark-Simulacrum

Update `catch_unwind` doc comments for `c_unwind`

Updates `catch_unwind` doc comments to indicate that catching a foreign exception _will no longer_ be UB. Instead, there are two possible behaviors, though it is not specified which one an implementation will choose.

Nominated for t-lang to confirm that they are okay with making such a promise based on t-opsem FCP, or whether they would like to be included in the FCP.

Related: rust-lang#74990, rust-lang#115285, rust-lang/reference#1226
bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 29, 2024
…=Mark-Simulacrum

Update `catch_unwind` doc comments for `c_unwind`

Updates `catch_unwind` doc comments to indicate that catching a foreign exception _will no longer_ be UB. Instead, there are two possible behaviors, though it is not specified which one an implementation will choose.

Nominated for t-lang to confirm that they are okay with making such a promise based on t-opsem FCP, or whether they would like to be included in the FCP.

Related: rust-lang#74990, rust-lang#115285, rust-lang/reference#1226
@BatmanAoD BatmanAoD force-pushed the c-unwind-documentation branch 3 times, most recently from 45da62b to aa9a282 Compare October 2, 2024 05:10
@BatmanAoD
Copy link
Member Author

Links should be fixed now, and everything has been rebased/squashed.

I'm not sure if any of the still-open threads should be considered blockers.

src/linkage.md Outdated Show resolved Hide resolved
RalfJung pushed a commit to RalfJung/miri that referenced this pull request Oct 3, 2024
…ulacrum

Update `catch_unwind` doc comments for `c_unwind`

Updates `catch_unwind` doc comments to indicate that catching a foreign exception _will no longer_ be UB. Instead, there are two possible behaviors, though it is not specified which one an implementation will choose.

Nominated for t-lang to confirm that they are okay with making such a promise based on t-opsem FCP, or whether they would like to be included in the FCP.

Related: rust-lang/rust#74990, rust-lang/rust#115285, rust-lang/reference#1226
Comment on lines +255 to +257
> Note: With `panic=unwind`, when a `panic` is turned into an abort by a
> non-unwinding ABI boundary, either no destructors (`Drop` calls) will run, or
> all destructors up until the ABI boundary will run.
Copy link

@daira daira Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This note (and the similar wording in src/destructors.md) address the uncertainty I had in a previous review about what code can potentially run before an abort, thankyou.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're welcome, but be careful, because the behavior in 1.81 is different from what we intend to ship in 1.82. When you asked before, this had been left intentionally undefined, but the lang team later signed off on the guarantee given in the text here. I had forgotten that the linked PR didn't actually make it into 1.81, though.

lnicola pushed a commit to lnicola/rust-analyzer that referenced this pull request Oct 8, 2024
…ulacrum

Update `catch_unwind` doc comments for `c_unwind`

Updates `catch_unwind` doc comments to indicate that catching a foreign exception _will no longer_ be UB. Instead, there are two possible behaviors, though it is not specified which one an implementation will choose.

Nominated for t-lang to confirm that they are okay with making such a promise based on t-opsem FCP, or whether they would like to be included in the FCP.

Related: rust-lang/rust#74990, rust-lang/rust#115285, rust-lang/reference#1226
@BatmanAoD
Copy link
Member Author

@traviscross I think this is ready for review again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: The marked PR is awaiting review from the PR author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.