-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
fix nounwind attribute logic #63909
fix nounwind attribute logic #63909
Conversation
r? @estebank (rust_highfive has picked a reviewer for you, use r? to override) |
src/test/codegen/extern-functions.rs
Outdated
@@ -6,14 +6,36 @@ | |||
extern { | |||
// CHECK: Function Attrs: nounwind | |||
// CHECK-NEXT: declare void @extern_fn | |||
fn extern_fn(); | |||
// CHECK-NOT: Function Attrs: nounwind | |||
fn extern_fn(); // assumed not to unwind |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part of the test seems dubious to me. Wouldn't code that bubbles Rust panics through C (the kind of code for which #62603 got merged) also have an extern "C"
declaration like this where the panic enters back into Rust?
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have a stable vs nightly difference in semantics. That would be a bad mess.
We document panicking from extern "C"
as UB, but (similar to #62825) are willing to patch things such that code exploiting this UB is not broken until we provide adequate alternatives to said code.
So, I think this function should not be unwind
. I am waiting for @alexcrichton to confirm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I think this function should not be unwind
This is right. The check should say CHECK-NOT: nounwind
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is correct, all imported FFI functions, by default, should be tagged with nounwind
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But doesn't this mean that when e.g. mozjpeg calls C that calls Rust (the situation due to which #62603 was accepted), and a panic passes from C to Rust, we still violate nounwind
?
This PR removes the nounwind
on the first edge the panic crosses (Rust -> C), but there's another edge (C -> Rust) and we should not have nounwind
there either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR removes the nounwind on the first edge the panic crosses (Rust -> C), but there's another edge (C -> Rust) and we should not have nounwind there either.
Yes, that's correct, that is, the check should be CHECK-NOT: nounwind
. Otherwise, the second example in #63943, which is equivalent to mozjpeg use case, would still be miscompiled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I have implemented that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I've left some comments, under the assumption that this primarly targets master, and therefore Rust nightly. Some of the tests would be incorrect for beta/stable, because there the semantics differ. I mentioned that in the comments, but I don't know how best to adress that. I think it would probably make sense to add what makes sense for nightly here, and tune the tests for stable Rust when doing the beta backport.
src/test/codegen/extern-functions.rs
Outdated
@@ -6,14 +6,36 @@ | |||
extern { | |||
// CHECK: Function Attrs: nounwind | |||
// CHECK-NEXT: declare void @extern_fn | |||
fn extern_fn(); | |||
// CHECK-NOT: Function Attrs: nounwind | |||
fn extern_fn(); // assumed not to unwind |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
How do they differ? #62603 has landed on master and I am assuming will be backported to beta. So the semantics should be the same. |
Which mis-compilations does this fix? I do not know of any. |
On my machine using stable Rust 1.37.0: extern "C" fn bar() -> ! { panic!("lolz") }
extern "C" fn baz() -> i32 {
if let Ok(_) = std::panic::catch_unwind(|| bar() ) {
unsafe { std::hint::unreachable_unchecked() }
}
42
}
fn main() { std::process::exit((baz() != 42) as i32); } returns success with fn bar() -> ! { panic!("lolz") } // no extern "C" then it always works. If unwinding from This means that for // foo.cpp
using fn_type = void(*)();
[[ noreturn ]] extern "C" void foo(fn_type x);
[[ noreturn ]] extern "C" void foo(fn_type x) { x(); /* unreachable: */ throw 0; } // main.rs
extern "C" { fn foo(x: extern "C" fn() -> !) -> !; }
extern "C" fn bar() -> ! { panic!("lolz") }
extern "C" fn baz() -> i32 {
if let Ok(_) = std::panic::catch_unwind(|| unsafe { foo(bar) }) {
unsafe { std::hint::unreachable_unchecked() }
}
42
}
fn main() {
std::process::exit((baz() != 42) as i32);
} should also always return success, but like the example above, it returns success with That is, AFAICT, as long as we make |
Good catch!
This PR does fix that, doesn't it? |
I don't know, no idea who decides that. I thought this might at least be interesting enough for that.
I haven't tried a patched compiler - will try when this lands on nightly unless somebody beats me to it. |
@gnzlbg Note that we've removed the abort-unwind behavior difference between nightly/beta/stable, per #62505 (comment) -> #62603. |
@rust-lang/compiler this is outside of my wheelhouse. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In its current state this seems like it's undoing an optimization which has been in rustc for quite some time? I was under the impression that it was well known that extern
imported functions (and defined functions in Rust) are undefined behavior if they unwind. This looks to be fixing that situation but this has historically been a relatively important optimization for some scenarios.
@rfcbot concern lets-not-increase-de-facto-reliance-meanwhile I noted in #63909 (comment) and in conversations with @joshtriplett (where I also said I'd leave such a concern on FCP-merge...) that:
Also, situations like
I don't respect the existing UB-exploiting code. I think I've made that clear before that I do not agree with "people need to do their jobs" based reasoning. On a social level, I find that objectionable behavior to do so knowingly and it leads to "tragedy of the commons" situation. There are differences of opinion on the language which is why there are contradicting signals. What I think we're really agreeing to at the moment is "keeping the status quo". I should note that when I FCP-ed #58794 (comment), I expected this to be a mere formality.
@gnzlbg seems to be of a different opinion re.
I don't think that not actively facilitating reliance on spec-UB is "teaching users a lesson".
When you have a single compiler, not-rustc-UB tends to translate into not-spec-UB over time.
That would be my preference but that option seems foreclosed until we have a stable alternative.
I disagree with this rationale. First, I think it puts pressure on the wrong people. Second, I think this will contribute to stress and rushing through a sub-optimal solution because time is running out. I don't think either of those are a good idea. I would not like to spend further language team meeting time discussing this as it seems clear to me that it would not change things and the time is better spent on other things, e.g. figuring out a stable alternative. |
Maybe this is just a poor choice of phrase, but are you suggesting that there are people on whom there should be more pressure? If so, why?
I haven't heard this phrase before; do you mind explaining what kind of reasoning you're objecting to? |
What I think is that more pressure should not be laid on the folks who benefit from the optimization. Keeping the status-quo of pressure on folks who do rely on what we have said is UB is (acceptable) fallout from that.
I object to the reasoning that it's socially acceptable to rely on things that are explicitly UB "because there's no other way to do it" and that "folks need to do their jobs". Lotsa folks need to do their job but such a reliance may be detrimental to other users, the lang team, etc. Also, who determines "what you want to do is reasonable"? Folks have varying opinions on that... e.g. some think a fully stable ABI is reasonable (I don't). |
I see no parallel here. No
Then we should add the abort-on-panic shim and call it done. The current situation is not a middle-ground. It is a "both sides are worse off" kind of situation.
Well, I am not sure which other terms to use for your argument here. The main argument I could extract so far is "if we don't do this, it could happen that someone else might also start doing what mozjpeg already does". I think that argument can be rephrased as "we should use de-facto (not de-jure!) UB as a teaching device" (the kind of teaching device that hurts you when you do the wrong thing). Maybe I missed other arguments you made? FWIW, this argument I mentioned is not backed by data (quite the contrary: it's been many months since we first reverted the abort-on-panic shim and no new users came up), and I don't even see the thing it wants to prevent as a big problem (we are not changing our intentions to re-add the attribute once the shim is back in place). I see miscompilations in production code as a much bigger problem.
I see little to no evidence for that. In the UCG discussions, a lot of the UB we are considering is not-de-facto UB. For example, barely any of our aliasing rules are de-facto UB right now, and still the general reception of Miri being very strict about checking these rules is positive.
Language design involves (amongst others) trade-offs between the interests of the language designers and its users -- and in your consideration, I see no indication that user's interests are taken into account. I would be entirely on-board with your reasoning if this was a research language, but I think for a language that wants to be a foundational piece of infrastructure, this kind of no-compromise attitude will do more harm than good. The picture you are painting of Rust users is an adversarial one: if we give in an inch by not making LLVM exploit this UB any more, they'll jump on that and completely disregard the intentions behind that change. This picture does not at all reflect my experience. Most of the time my impression is that users understand the language design concerns around UB, and understand that we want to keep our options open; my discussions along those lines have mostly been constructive (there are exceptions, of course). Users don't want to halt Rust language development, but they also want to use Rust to do "something" and sometimes, only after doing all the work they realize that what they want to do is not (de-jure) possible in Rust. To speak more concretely, I am regularly talking with users about Stacked Borrows or validity invariants; much of that is de-jure but not de-facto UB and it is also much more fuzzy than So, I think your worry is unfounded. I doubt there will be a lot of pushback against re-adding the attribute and the abort-on-panic shim once one of the RFCs lands; if past experience tells us anything, people will just be happy that finally there are some proper rules for what they want to do. |
Thank you very much for your comment. I do have one small correction.
There are several users of cross-language unwinding participating in RFC 2753 (though to be fair, they may have been doing so since before the abort revert). In particular, @acfoltzer has explained how Lucet uses this functionality and is helping draft the RFC text itself. |
As one of the people who would benefit in the interim from the lack of |
So when I originally skimmed over this PR and its description, I was under the impression that it only addressed bugs in "safe code" that arose in cases where you had Rust calling into C (and then from there potentially back into Rust, which then may be the cause of a root panic). And that impression may or may not be correct. I'm not sure yet ... ... but what I do know is that this fixes a bug in some code which has no such back-and-forth calling between Rust and C (at least not at the surface level; though there may be some in the stdlib), namely this example adapted from #64655: struct Droppable;
impl Drop for Droppable { fn drop(&mut self) { println!("Dropping a Droppable"); } }
fn main() { let _guard = Droppable; None::<()>.expect("???"); } I'd like to understand why this PR fixes that bug. But in the meantime, even absent such understanding on my part, I also think we should just land this PR. The case illustrated above is not some subtle interaction between C and Rust; at least, not from the user's perspective. |
Okay thanks to the comment thread on this issue I have lots of fun variants on my previous example.
|
…arking-rust-abi-unwind-issue-64655, r=alexcrichton Always mark rust and rust-call abi's as unwind PR rust-lang#63909 identified a bug that had been injected by PR rust-lang#55982. As discussed on rust-lang#64655 (comment) , we started marking extern items as nounwind, *even* extern items that said they were using "Rust" or "rust-call" ABI. This is a more targeted variant of PR rust-lang#63909 that fixes the above bug. Fix rust-lang#64655 ---- I personally suspect we will want PR rust-lang#63909 to land in the long-term But: * it is not certain that PR rust-lang#63909 *will* land, * more importantly, PR rust-lang#63909 almost certainly will not be backported to beta/stable. The identified bug was more severe than I think anyone realized (apart from perhaps @gnzlbg, as noted [here](rust-lang#63909 (comment))). Thus, I was motivated to write this PR, which fixes *just* the issue with extern rust/rust-call functions, and deliberately avoids injecting further deviation from current behavior (you can see further notes on this in the comments of the code added here).
…arking-rust-abi-unwind-issue-64655, r=alexcrichton Always mark rust and rust-call abi's as unwind PR rust-lang#63909 identified a bug that had been injected by PR rust-lang#55982. As discussed on rust-lang#64655 (comment) , we started marking extern items as nounwind, *even* extern items that said they were using "Rust" or "rust-call" ABI. This is a more targeted variant of PR rust-lang#63909 that fixes the above bug. Fix rust-lang#64655 ---- I personally suspect we will want PR rust-lang#63909 to land in the long-term But: * it is not certain that PR rust-lang#63909 *will* land, * more importantly, PR rust-lang#63909 almost certainly will not be backported to beta/stable. The identified bug was more severe than I think anyone realized (apart from perhaps @gnzlbg, as noted [here](rust-lang#63909 (comment))). Thus, I was motivated to write this PR, which fixes *just* the issue with extern rust/rust-call functions, and deliberately avoids injecting further deviation from current behavior (you can see further notes on this in the comments of the code added here).
…arking-rust-abi-unwind-issue-64655, r=alexcrichton Always mark rust and rust-call abi's as unwind PR rust-lang#63909 identified a bug that had been injected by PR rust-lang#55982. As discussed on rust-lang#64655 (comment) , we started marking extern items as nounwind, *even* extern items that said they were using "Rust" or "rust-call" ABI. This is a more targeted variant of PR rust-lang#63909 that fixes the above bug. Fix rust-lang#64655 ---- I personally suspect we will want PR rust-lang#63909 to land in the long-term But: * it is not certain that PR rust-lang#63909 *will* land, * more importantly, PR rust-lang#63909 almost certainly will not be backported to beta/stable. The identified bug was more severe than I think anyone realized (apart from perhaps @gnzlbg, as noted [here](rust-lang#63909 (comment))). Thus, I was motivated to write this PR, which fixes *just* the issue with extern rust/rust-call functions, and deliberately avoids injecting further deviation from current behavior (you can see further notes on this in the comments of the code added here).
…-abi-unwind-issue-64655, r=alexcrichton Always mark rust and rust-call abi's as unwind PR #63909 identified a bug that had been injected by PR #55982. As discussed on #64655 (comment) , we started marking extern items as nounwind, *even* extern items that said they were using "Rust" or "rust-call" ABI. This is a more targeted variant of PR #63909 that fixes the above bug. Fix #64655 ---- I personally suspect we will want PR #63909 to land in the long-term But: * it is not certain that PR #63909 *will* land, * more importantly, PR #63909 almost certainly will not be backported to beta/stable. The identified bug was more severe than I think anyone realized (apart from perhaps @gnzlbg, as noted [here](#63909 (comment))). Thus, I was motivated to write this PR, which fixes *just* the issue with extern rust/rust-call functions, and deliberately avoids injecting further deviation from current behavior (you can see further notes on this in the comments of the code added here).
Looks like we are unable to decide that we want to protect users like mozjpeg from UB while the RFC But I guess there's no point in leaving this PR open, then. |
…king-rust-abi-unwind-issue-64655, r=alexcrichton Always mark rust and rust-call abi's as unwind PR rust-lang#63909 identified a bug that had been injected by PR rust-lang#55982. As discussed on rust-lang#64655 (comment) , we started marking extern items as nounwind, *even* extern items that said they were using "Rust" or "rust-call" ABI. This is a more targeted variant of PR rust-lang#63909 that fixes the above bug. Fix rust-lang#64655 ---- I personally suspect we will want PR rust-lang#63909 to land in the long-term But: * it is not certain that PR rust-lang#63909 *will* land, * more importantly, PR rust-lang#63909 almost certainly will not be backported to beta/stable. The identified bug was more severe than I think anyone realized (apart from perhaps @gnzlbg, as noted [here](rust-lang#63909 (comment))). Thus, I was motivated to write this PR, which fixes *just* the issue with extern rust/rust-call functions, and deliberately avoids injecting further deviation from current behavior (you can see further notes on this in the comments of the code added here).
With #62603 landed, we no longer add abort-on-panic shims to
extern "C"
functions. However, that means we should also not emitnounwind
forextern fn
because we are not actually catching those panics. The comment justifying thatnounwind
in the source is just factually wrong.I also noticed that we annotate
extern
declarations (of any ABI) withnounwind
, which seems wrong? I removed this. Code like mozjpeg (the code that prompted the removal of the abort-on-panic shim) throws a panic from Rust code that enters C code (meaning theextern "C"
definition must not havenounwind
, or else we got UB), but then the panic bubbles through the C code and re-enters Rust code (meaning theextern "C"
declaration that gets called from the Rust side must not havenounwind
either).This should be beta-backported if #62603 gets backported.
TODO: add test case for #63943.