Redefine c_void#159935
Conversation
e913fbe to
399567c
Compare
The current definition is based on legacy cruft which no longer matters. Redefine the type to be maximally forgiving in most cases, but maximally strict against invalid usage in Miri.
399567c to
e052a52
Compare
|
This change:
|
You would have to obtain a value of type
It's not visibly uninhabited, so it shouldn't affect what code compiles (e.g.
Yes, |
I feel like based upon the definition, you could technically use |
|
You would have to know that 0 was a valid discriminant, which I believe was not observable, and could have changed at any time, if std wanted to. |
|
Any special-casing of Miri in the standard library requires review. cc @rust-lang/miri
cc @rust-lang/miri
cc @rust-lang/rust-analyzer |
|
r? @Darksonn rustbot has assigned @Darksonn. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
The job Click to see the possible cause of the failure (guessed by this bot) |
|
I've verified that this change doesn't affect the CFI sanitizer, which is good. I added #159950 for some missing tests in this regard. |
| )] | ||
| #[doc(hidden)] | ||
| __variant2, | ||
| pub struct c_void { |
There was a problem hiding this comment.
This changes the doc path for this type from core/ffi/enum.c_void.html to core/ffi/struct.c_void.html. If we make this change, we should introduce a redirect to keep links to the old path working.
| // for backward compatibility. | ||
| #[stable(feature = "core_c_void", since = "1.30.0")] | ||
| impl RefUnwindSafe for c_void {} |
There was a problem hiding this comment.
This PR removes the following trait implementations:
FreezeUnpinUnsafeUnpin
One of these is a stable trait, so this is a breaking change. Should we also implement Unpin?
There was a problem hiding this comment.
Yes, that was an oversight, good catch
|
I think it is a bad idea to make this type depend on |
| // However, if running in Miri, | ||
| // we want to maximize detection of UB, | ||
| // so we make `c_void` uninhabited. |
There was a problem hiding this comment.
I don't think this is worth the risk of divergence from the behavior during regular compilation.
There was a problem hiding this comment.
Compare the risks of not doing this. Either we:
- Use the permissive definition everywhere, so Miri stops flagging misuse that happens in practice and that it currently flags, and such misuse becomes more common
- Use the strict definition everywhere, so code that in the past was UB or almost-UB but happened to work, now triggers real nasal demons
- Stick with the current fragile middle ground, where there is a bunch of almost-UB—which Miri won't flag, potentially giving people false confidence, but it's very fragile and easy to turn into full UB
I think the theoretical downside of us maybe having overlooked some way in which the cfg(miri) version has less UB, is outweighed by these practical and certain downsides.
There was a problem hiding this comment.
Miri is not a general tool to detect misuse. It is a very specific tool to detect language UB. Whatever misuse they do is apparently not language UB, so Miri is the wrong tool to find it. I am fine with having extra checks under if cfg!(miri) to detect bugs early (though usually those should then probably also be enabled as debug assertions), as those are obviously correct. But I am not fine with using entirely different type definitions under cfg!(miri).
I understand your arguments in favor of this change, but I don't think they are convincing enough. There are way too many things that happen inside the compiler that may go different for those different type definitions, that's just not a game I am willing to play. That's still a clear "thanks but no" from my side for any cfg!(miri) in this type definition.
There was a problem hiding this comment.
Whatever misuse they do is apparently not language UB
Plenty is! And plenty isn't only because of unspecified details of the current layout.
If you don't like the approach this PR takes, which of the alternatives I listed would you prefer?
There was a problem hiding this comment.
I think that's up to libs or libs-api to decide, I don't have a strong opinion beyond "Miri should check the actual code we usually run".
There was a problem hiding this comment.
"Miri should check the actual code we usually run".
Note that this is almost certainly not happening anyway, because c_void is intended for FFI, so code using it run under Miri is likely using some sort of mocking.
There was a problem hiding this comment.
It needs to mock the C side, but the Rust side should be faithful.
There was a problem hiding this comment.
How would you feel about making the cfg(miri) definition identical to the cfg(not(miri)), except for an additional !-typed field?
There was a problem hiding this comment.
That's still far from identical as such a field makes quite the difference for layout computation.
Might might do ill-advised nonsense like Option<c_void> and whatever. I don't want that to start doing things in Miri that it wouldn't do outside Miri.
There was a problem hiding this comment.
And we can't just make it straight-up uninhabited always, right? Because that breaks other stuff/makes it incorrect? (I haven't thought about this long enough to remember the answer, it's out of cache apparently.)
| )] | ||
| #[doc(hidden)] | ||
| __variant2, | ||
| pub struct c_void { |
There was a problem hiding this comment.
mejrs makes a good point on zulip
It's also a breaking change to change it back from a struct to enum if we ever need to, even if all fields are private you can make a
c_void { .. }pattern
Let's add a test using this pattern so that we catch it when/if this changes. (Even if we don't make this change, we should add the test.)
There was a problem hiding this comment.
If you are doing this, that means you a
are matching on a value or reference to c_void, which means your code is essentially guaranteed to be wrong (UB or near-UB).
There was a problem hiding this comment.
Not necessarily. The code might just be dead code.
…rtdev Add CFI tests for return types and never type In a few different conversations, the CFI types of various functions has come up. First, the CFI type of returning `!` came up in rust-lang#159446 (comment), where we found that it doesn't have the same CFI type as returning `()`, which it probably should. Then in rust-lang#159935, I wanted to make sure that this does not change the CFI type of functions when `*mut c_void` appears as an argument. Luckily it does not since it's a lang item, but there's no test for this case. Thus, add tests for all of these cases and a few others. AI assistance was involved with writing the test.
The current definition is based on legacy cruft which no longer matter. Let's make it somewhat saner:
UnsafePinnedandMaybeUninitto make it as difficult as possible to cause UB with the type.c_voiduninhabited. That way, any attempt to construct a value ofc_void, or a reference to it, gets flagged. (Such code is essentially guaranteed to be wrong, asc_voidshould only be used behind a raw pointer. However, people love to do it anyway. We should add a lint at some point…)@rustbot label A-FFI A-miri