-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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 on invalid usages of MaybeUninit::uninit().assume_init()
#100423
Conversation
(rust-highfive has picked a reviewer for you, use r? to override) |
Hey! It looks like you've submitted a new PR for the library teams! If this PR contains changes to any Examples of
Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt |
This comment has been minimized.
This comment has been minimized.
Also gonna leave a linkback to #66151 which this PR is relevant to... Kinda! |
Heads up, with |
r? rust-lang/mir |
Did some investigation with @Nilstrieb and it seems to be their problem. We're inserting panics in a place they're not panic safe. They deadlock. Happens on stable if you insert a panic manually. |
3511f28
to
8ee3fa5
Compare
continue; | ||
}; | ||
|
||
let Operand::Move(assume_init_place) = assume_init_operand else { |
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.
let Operand::Move(assume_init_place) = assume_init_operand else { | |
let Some(assume_init_place) = assume_init_operand.place() else { |
This passes in your tests because MIR only ever sees the generic T
type due to the wrapper functions. However, if you add a T: Copy
bound, the tests that still compile should fail.
Can you add an mir-opt test that demonstrates this transform, including for both moves and copies?
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 can't figure out how to make it generate a Copy
.
This will take a little time as I don't have that much time right now, so @rustbot author |
Honestly I am not sure this is the right move. With a panic like this, the chances are pretty high that they will hit people that can do absolutely nothing about them -- because the panic is caused by a dependency. And having to implement this via a MIR pass sounds like the kind of hack that will be quite annoying to maintain. I think I'd be more in favor of using a lint for this. And note that the |
One advantage the panics have over a lint is the lint needs to work pre-monomorphisation, so we can't lint for making an uninit And the lint warns about dead code, which is arguably a benefit (don't need to actually test the code, you get immediate feedback). The panics run where we know the type layout concretely, so we can accurately panic even in cases of zeroed enums where the variant with discriminant zero doesn't allow being left zeroed. That would be very hard, if not impossible, to use a lint for. That's not to say our lints couldn't be improved, but I think the panics serve a useful purpose by saying you did do a UB. Both are useful in different circumstances, which is why I think we should have both. |
8ee3fa5
to
323c7a6
Compare
I've addressed all the comments, but #101061 will force me to change some of the intrinsics used here if it's merged. This brings me to a question: Should we use |
This comment has been minimized.
This comment has been minimized.
If you replace "panic" in your statement with "UB", is the meaning much different? The dependency is behaving badly, which is hard for its users, but the panic at least makes it deterministic. |
Also, it's worth thinking about if we're trying to prevent just LLVM UB here, or rust UB? I agree that being minimal in our checks for (We did say "uhhhh uninit ints may be fine, they may not, be careful regardless", which has since been changed to "they're UB.") If it's rust UB, then strict-init-checks is the correct thing to do here. If it's LLVM UB, then we need to still panic on uninit |
…, r=RalfJung Rename `assert_uninit_valid` intrinsic It's not about "uninit" anymore but about "filling with 0x01 bytes" so the name should at least try to reflect that. This is actually not fully correct though, as it does still panic for all uninit with `-Zstrict-init-checks`. I'm not sure what the best way is to deal with that not causing confusion. I guess we could just remove the flag? I don't think having it makes a lot of sense anymore with the direction that we have chose to go. It could be relevant again if rust-lang#100423 lands so removing it may be a bit over eager. r? `@RalfJung`
Discussed in this week's @rust-lang/lang meeting (notes). There are some points that we would like to see addressed: Documentation. It should be as clear as possible how to take action on an abort caused by one of these checks. Documentation should be easy to find. When code that was perceived as "working" breaks, maintainers are less willing to put in effort to understand what is going on, and will often reach for the quickest fix available in the moment. Debuggability. As previously discussed, we should make sure it is possible to understand how this panic/abort is happening. If looking at the library source for Impact. We want to understand the level of impact on users, both library authors and consumers, and map out what options they will have if their program aborts (especially if it's in a library they don't control). We may want to add mitigation options, like a compiler flag that disables these checks. Best effort. We should communicate broadly and clearly that these checks are best-effort. We don't want users to rely on them as guarantees to prevent classes of UB, unless we can promise that and stick to that promise. It's possible some of these require further discussion. Feel free to do that on Zulip #t-lang for smaller questions or propose a design meeting for longer discussion. If you feel comfortable addressing them in this thread, go ahead and do so and re-nominate the PR. @rustbot label: -I-lang-nominated Finally, it was noted that the general concept of runtime UB detection is one that would benefit from broader discussion and communication. In particular, we would be receptive to an RFC that discusses an overall approach to runtime UB detection and touches on these questions with future expansions in mind, like options for users to add more expensive runtime checks to their programs. We don't necessarily consider that a blocker for this PR, but think that it would be a useful direction for Rust's tooling to evolve in and that it would be useful to do some initial thinking about how this PR fits into a broader picture. |
…, r=RalfJung Rename `assert_uninit_valid` intrinsic It's not about "uninit" anymore but about "filling with 0x01 bytes" so the name should at least try to reflect that. This is actually not fully correct though, as it does still panic for all uninit with `-Zstrict-init-checks`. I'm not sure what the best way is to deal with that not causing confusion. I guess we could just remove the flag? I don't think having it makes a lot of sense anymore with the direction that we have chose to go. It could be relevant again if rust-lang#100423 lands so removing it may be a bit over eager. r? `@RalfJung`
323c7a6
to
5562c09
Compare
This comment has been minimized.
This comment has been minimized.
…, r=RalfJung Rename `assert_uninit_valid` intrinsic It's not about "uninit" anymore but about "filling with 0x01 bytes" so the name should at least try to reflect that. This is actually not fully correct though, as it does still panic for all uninit with `-Zstrict-init-checks`. I'm not sure what the best way is to deal with that not causing confusion. I guess we could just remove the flag? I don't think having it makes a lot of sense anymore with the direction that we have chose to go. It could be relevant again if rust-lang#100423 lands so removing it may be a bit over eager. r? `@RalfJung`
This MIR transform inserts the same validity checks from `mem::{uninitialized,zeroed}` to `MaybeUninit::{uninit,zeroed}().assume_init()`. We have been panicking in `mem::uninit` on invalid values for quite some time now, and it has helped to get people off the unsound API and towards using `MaybeUninit<T>`. While correct usage of `MaybeUninit<T>` is clearly documented, some people still use it incorrectly and simply replaced their wrong `mem::uninit` usage with `MaybeUninit::uninit().assume_init()`. This is not any more correct than the old version, and we should still emit panics in these cases. As this can't be done in the library only, we need this MIR pass to insert the calls. For now, it only detects direct usages of `MaybeUninit::uninit().assume_init()` but it could be extended in the future to do more advanced dataflow analysis.
5562c09
to
641251b
Compare
The job Click to see the possible cause of the failure (guessed by this bot)
|
…, r=compiler-errors Unify validity checks into a single query Previously, there were two queries to check whether a type allows the 0x01 or zeroed bitpattern. I am planning on adding a further initness to check in rust-lang#100423, truly uninit for MaybeUninit, which would make this three queries. This seems overkill for such a small feature, so this PR unifies them into one. I am not entirely happy with the naming and key type and open for improvements. r? oli-obk
…, r=compiler-errors Unify validity checks into a single query Previously, there were two queries to check whether a type allows the 0x01 or zeroed bitpattern. I am planning on adding a further initness to check in rust-lang#100423, truly uninit for MaybeUninit, which would make this three queries. This seems overkill for such a small feature, so this PR unifies them into one. I am not entirely happy with the naming and key type and open for improvements. r? oli-obk
…, r=compiler-errors Unify validity checks into a single query Previously, there were two queries to check whether a type allows the 0x01 or zeroed bitpattern. I am planning on adding a further initness to check in rust-lang#100423, truly uninit for MaybeUninit, which would make this three queries. This seems overkill for such a small feature, so this PR unifies them into one. I am not entirely happy with the naming and key type and open for improvements. r? oli-obk
…ompiler-errors Allow checking whether a type allows being uninitialized This is useful for clippy ([rust-lang/clippy#10407](rust-lang/rust-clippy#10407)) and for the future `MaybeUninit::assume_init` panics (rust-lang#100423).
…, r=compiler-errors Unify validity checks into a single query Previously, there were two queries to check whether a type allows the 0x01 or zeroed bitpattern. I am planning on adding a further initness to check in rust-lang#100423, truly uninit for MaybeUninit, which would make this three queries. This seems overkill for such a small feature, so this PR unifies them into one. I am not entirely happy with the naming and key type and open for improvements. r? oli-obk
☔ The latest upstream changes (presumably #110324) made this pull request unmergeable. Please resolve the merge conflicts. |
Closing this as it has hit a deadend and there's no motivation of completing it |
This MIR transform inserts the same validity checks from
mem::{uninitialized,zeroed}
toMaybeUninit::{uninit,zeroed}().assume_init()
.We have been panicking in
mem::uninit
on invalid values for quite some time now, and it has helped to get people off the unsound API and towards usingMaybeUninit<T>
.While correct usage of
MaybeUninit<T>
is clearly documented, some people still use it incorrectly and simply replaced their wrongmem::uninit
usage withMaybeUninit::uninit().assume_init()
. This is not any more correct than the old version, and we should still emit panics in these cases. As this can't be done in the library only, we need this MIR pass to insert the calls.For now, it only detects direct usages of
MaybeUninit::uninit().assume_init()
but it could be extended in the future to do more advanced dataflow analysis.