explicitly keep the door open for some but not all subobject provenance - #2338
explicitly keep the door open for some but not all subobject provenance#2338RalfJung wants to merge 1 commit into
Conversation
d265a11 to
fe2f637
Compare
fe2f637 to
019ee5a
Compare
|
|
||
| Generally, a reference may only access the memory it [points to]. | ||
| This restriction also applies to all raw pointers derived from this reference. | ||
| The one exception is that a reference to an element of an array or slice may be used to access other elements of the same array or slice without immediately causing undefined behavior. |
There was a problem hiding this comment.
How does this play with transmutes? Consider:
#[repr(C)]
struct Pair {
first: [usize; 4],
second: [usize; 4],
}
let pair = &Pair { ... };
let arr: &[usize; 8] = unsafe { &*(pair as *const Pair) };
let i0: *const usize = &arr[0];
let i7 = unsafe { i0.add(7) };
let i7 = unsafe { &*i7 };This goes from:
&Pairto&[usize; 8](a sound transmute)- ...to a raw pointer to the 0th element
- ...to a raw pointer to the 7th element
- ...to a reference to the 7th element
Presumably if we skipped the &Pair -> &[usize; 8] step and instead constructed i0 from &pair.first[0], this would be unsound because it would entail jumping between first and second.
Two questions:
- Am I correct that the code as written is sound, and that if we skipped
&Pair -> &[usize; 8], it would not be sound? - If so, what accounts for the differing soundness between these two examples?
| Generally, a reference may only access the memory it [points to]. | ||
| This restriction also applies to all raw pointers derived from this reference. | ||
| The one exception is that a reference to an element of an array or slice may be used to access other elements of the same array or slice without immediately causing undefined behavior. | ||
| This exception also applies for nested arrays, but not for fields of values inside arrays. |
There was a problem hiding this comment.
Can we clarify what "this exception also applies for nested arrays" means? I presume it means that you can go from e.g. x[0][0] to x[1][1] (where x: [[usize; 2]; 2])? What about from x[0] to x[1][1] or from x[0][0] to x[1]?
| This exception also applies for nested arrays, but not for fields of values inside arrays. | ||
|
|
||
| Furthermore, a reference to a field of an enum may not be used to change the discriminant of said enum. | ||
| If the tag lies inside the range of memory accessible by the reference (as in the previous paragraph), then the reference may be used to *temporarily* change the discriminant of the enum without immediately causing undefined behavior, but the original discriminant must be restored before the lifetime of the reference ends. |
There was a problem hiding this comment.
What does "as in the previous paragraph" refer to?
| This exception also applies for nested arrays, but not for fields of values inside arrays. | ||
|
|
||
| Furthermore, a reference to a field of an enum may not be used to change the discriminant of said enum. | ||
| If the tag lies inside the range of memory accessible by the reference (as in the previous paragraph), then the reference may be used to *temporarily* change the discriminant of the enum without immediately causing undefined behavior, but the original discriminant must be restored before the lifetime of the reference ends. |
There was a problem hiding this comment.
What if changing the discriminant has the effect of just doing a transmute? E.g.:
#[repr(u8)]
enum Zero { Zero = 0 }
#[repr(u8)]
enum One { One = 1 }
enum Bit {
Zero(Zero),
One(One),
}If Rust chooses to niche-optimize Bit so that there's no explicit discriminant, then given *mut Zero pointing to the only field of Bit::Zero, you could overwrite it with One::One and you'd effectively have transmuted the entire Bit to Bit::One(One::One). Is this sound even if the discriminant is not restored?
| This exception also applies for nested arrays, but not for fields of values inside arrays. | ||
|
|
||
| Furthermore, a reference to a field of an enum may not be used to change the discriminant of said enum. | ||
| If the tag lies inside the range of memory accessible by the reference (as in the previous paragraph), then the reference may be used to *temporarily* change the discriminant of the enum without immediately causing undefined behavior, but the original discriminant must be restored before the lifetime of the reference ends. |
There was a problem hiding this comment.
We often say that lifetimes are not relevant to opsem. What does the term "lifetime" mean in this context? Is this about crossing an API boundary (i.e., this is really a safety thing about what you're allowed to assume when an enum reference/pointer crosses an API boundary), or is this about SB/TB, or something else?
Miri has enforced fairly strict subobject provenance by default since ~forever, but the Reference never explicitly called this out as UB. Let's fix that. This is the conservative choice; we document this as UB now and maybe lift the UB restriction again in the future.
This PR includes two commitments that @rust-lang/opsem (and maybe @rust-lang/lang) should FCP:
I proposed for opsem to FCP these two choices in:
This PR should only be merged once both of those FCP completed.