consider assignments of union field of ManuallyDrop type safe - #78068
Conversation
|
r? @eddyb (rust_highfive has picked a reviewer for you, use r? to override) |
There was a problem hiding this comment.
What slightly concerns me here is that this is the context of the entire place, but we are considering here just a single place projection. What if there is a deref involved?
#![feature(untagged_unions)]
union U {
p: &'static mut i32,
}
fn test(u: U) {
*(u.p) = 13;
}This does error, but the error claims "assignment to non-Copy union field", which is not really what happens (the value of p is not changed). The problem is that this is reading a union field. If I made the test here even smarter to take into account needs_drop, then that code above would be incorrectly accepted!
In fact, union fields that drop are not even permitted any more (with no amount of feature gates).
There was a problem hiding this comment.
Fixed this by checking for Deref explicitly. The logic is kind of strange now but I think it works -- and I added a test to confirm that the test above how fails with the right error:
error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:31:5
|
LL | *(u.p) = 13;
| ^^^^^^^^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
(instead of "assignment to union field")
There was a problem hiding this comment.
This leads to the funny situation where, inside core::mem::manually_drop, unions are unsound, because we can do things like
union U {
f: ManuallyDrop<Vec<i32>>,
}
fn test(u: U) {
u.f.value = Vec::new();
}It's a bit awkward to be checking the type of the field here when really we care about the type of the assignment (which I guess is the type of the overall place).
There was a problem hiding this comment.
Fixed this by checking the actually assigned type, not the type of the projection.
|
Nominating for lang-team discussion. |
f4cbb6d to
6de3f2b
Compare
There was a problem hiding this comment.
This strikes me as rather suboptimal formatting, bur rustfmt insists on it...
There was a problem hiding this comment.
Ouch. This is poor even in isolation, but with the || chain it's terrible 🙁
You're in the compiler, so I guess you could try matches!(context, PlaceContext::MutatingUse(MutatingUseContext::Store | MutatingUseContext::Drop | MutatingUseContext::AsmOutput)?
There was a problem hiding this comment.
I reported this against rustfmt: rust-lang/rustfmt#4492
There was a problem hiding this comment.
Your proposal is formatted slightly better:
let assign_to_field = matches!(
context,
PlaceContext::MutatingUse(
MutatingUseContext::Store
| MutatingUseContext::Drop
| MutatingUseContext::AsmOutput
)
);Still not great, but the three alternatives do not fit on one line so it is not easy to make this nice.
There was a problem hiding this comment.
nit: You mention on line 25 that the field doesn't actually need dropping, so I wonder if there's a way to rephrase this to avoid a potential "no it doesn't" reaction from someone reading it. The best that comes to mind is "... that is neither Copy nor ManuallyDrop<_>", but that's not great either.
There was a problem hiding this comment.
I considered "that might need dropping", what do you think about that?
There was a problem hiding this comment.
I guess if I take a step back the answer might be that this is still unstable so it doesn't really matter.
Reading the tracking issue more, it seems like this would only be stabilized with a "this will never be drop in the future" trait, so if that were to happen then this message could just mention that.
So I'll just call this resolved.
(Hmm, impl !Drop for Foo{} got implemented, didn't it...)
|
This sounds good to me. I agree that it can be safe (which is itself a weak should), and I don't think it's materially more likely to lead to accidental leaking than other things that have already been made safe. Notably, if you have And the fact that it's wrapped in So while we haven't actually talked about this in a meeting yet, let's see what happens with |
|
Team member @scottmcm has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
commented
Oct 26, 2020
|
This seems fine to me. You can't implicitly turn a |
commented
Oct 27, 2020
|
@rfcbot reviewed |
commented
Oct 27, 2020
|
🔔 This is now entering its final comment period, as per the review above. 🔔 |
commented
Nov 6, 2020
|
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC will be merged soon. |
commented
Nov 14, 2020
|
@scottmcm can you take over review of this PR? |
a068d7c to
af309cc
Compare
|
@nikomatsakis I did (a version of) the refactoring you asked for. I think the new I also disentangled the place safety checks, so it's no longer one big mess checking 3 or 4 safety conditions at once. I found that really hard to follow. Unfortunately, for some reason I cannot figure out, this change leads to the unsafety error for accessing mutable statics to be raised twice, with slightly different spans, as you can see in the "static-mut-foreign-requires-unsafe" test. Any idea why that might be happening? Cc @oli-obk |
f98d6c3 to
615f83b
Compare
commented
Nov 22, 2020
This is because |
commented
Nov 22, 2020
Hm, I see... that does seem rather fragile TBH. But I guess we have tests. I'll try to work with this, thanks! |
615f83b to
6b739c2
Compare
commented
Nov 22, 2020
|
Yeah that seems to work, the "unsafe" and "static" tests works now. :) Let's see about the rest of the test suite. |
6b739c2 to
571da2c
Compare
commented
Nov 25, 2020
|
@RalfJung thanks for pursuing that. I'll take a look. |
| // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes | ||
| // whether we *read* the union field or potentially *write* to it (if this place is being assigned to). | ||
| let mut saw_deref = false; | ||
| for (base, proj) in place.iter_projections().rev() { |
There was a problem hiding this comment.
ah, cute, I missed that this was a double-ended iterator at first and wondered how you were going to manage this
| } | ||
|
|
||
| /// Iterate over the projections in evaluation order, i.e., the first element is the base with | ||
| /// its projection and then subsequently more projections are added. |
There was a problem hiding this comment.
I think it would be useful to give an example based on Rust code. For example:
Given the place a.b.c, this would yield:
(a, b)(a.b, c)
I am a bit surprised by this structure -- I guess I expected it to return a, a.b, and a.b.c, rather than a tuple, and to have people match on the "tail" projection (if any). But I guess this is ok too.
There was a problem hiding this comment.
I expanded the comment.
I expected it to return a, a.b, and a.b.c, rather than a tuple, and to have people match on the "tail" projection (if any). But I guess this is ok too.
I first thought of something like this, but it doesn't really match what clients need, at least what this particular client needs. The point is to check the projections, so the iterator really should yield as often as there are projections. And given that it also seemed odd to not make the projection itself directly available.
In a follow-up PR I hope to port more clients to this API, I guess then we will see how generally useful it is.
commented
Dec 15, 2020
|
@bors r+ |
commented
Dec 15, 2020
|
📌 Commit 0bb82c4 has been approved by |
commented
Dec 15, 2020
commented
Dec 15, 2020
|
☀️ Test successful - checks-actions |
commented
Jan 1, 2021
|
Possibly |
Assigning to
Copyunion fields is safe because that assignment will never drop anything. However, with #77547, unions may also haveManuallyDropfields, and their assignments are currently still unsafe. That seems unnecessary though, as assigningManuallyDropdoes not drop anything either, and is thus safe even for union fields.I assume this will at least require FCP.