-
Notifications
You must be signed in to change notification settings - Fork 356
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
TB: Reserved + Protected + IM + lazy is a horrible combination that should not exist #3742
Conversation
|
||
fn example(spurious: bool) { | ||
// For this example it is important that we have at least two bytes | ||
// because lazyness is involved. |
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.
Why does that need two bytes? You can have 1 byte and an &()
reference, and then laziness is still visible, no?
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 ()
is not interior mutable. In general, using ZSTs here suggests the counterexample relies on "wonky" ZST semantics, even worse if we cook up an Interior mutable ZST type (UnsafeCell<()>
), because what does that even mean? It would make the thing actually happening here much more obscure. And also, the counterexample is independent of whatever happens at ZST retags. It still occurs when the language has no notion of zero-sized objects at all.
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.
What makes an UnsafeCell<()>
accessed outside its range more wonky than an UnsafeCell<u8>
outside its range?
The counterexample is independent of what happens in one of the two bytes that you have here, so having both bytes is somewhat confusing. It's not about zero-sized objects, it's about a reference that only has "lazy" accesses.
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.
Because if you have an array of UnsafeCell<u8>
and go to the next element you naturally do an actual memory access, but if you iterate an array of UnsafeCell<()>
you're actually doing no memory accesses at all.
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.
Oh I think I just understood the point. We wouldn't have two elements, we'd just have one single Cell<u8>
that we retag as &mut *(r as *Cell<u8> as *Cell<()>) as *mut Cell<()> as *mut Cell<u8>
I guess ?
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.
@JoJoDeveloping not sure why array iteration would suddenly be relevant?
@Vanille-N yes.
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 that @JoJoDeveloping interpreted your comment "you can have 1 byte and a &()
reference" similarly to what I first did. Initially I thought you were arguing for using (Cell<()>, Cell<u8>)
rather than [Cell<u8>; 2]
but actually it would just be a single Cell<u8>
.
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.
Yes I disagree with it being a single byte allocation.
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 don't understand your reasoning for that. There's a single byte that's actually relevant for this, and all this mucking about with shifting the pointer back and forth is a distraction. The key point is that the relevant byte is not inside the range that is initialized upon reborrow.
- split test into two revisions - clarify comments
Btw the exhaustive tests need to be updated, but it's a bit more work because now not all configurations are valid as initial permissions. There's nothing incorrect right now because we're just testing more configurations. |
|
|
It does indeed not caue problems. The only other place it's relevant is on |
Test LGTM now, thanks!
What is the plan with this? Should it happen in this PR or a future PR? |
Co-authored-by: Ralf Jung <post@ralfj.de>
Worth noting that now |
It seems fine to not do that in this PR, but then please leave a FIXME at the relevant place(s) in the code. |
LGTM, thanks! |
☀️ Test successful - checks-actions |
Make unused states of Reserved unrepresentable In the [previous TB update](#3742) we discovered that the existence of `Reserved + !ty_is_freeze + protected` is undesirable. This has the side effect of making `Reserved { conflicted: true, ty_is_freeze: false }` unreachable. As such it is desirable that this state would also be unrepresentable. This PR eliminates the unused configuration by changing ```rs enum PermissionPriv { Reserved { ty_is_freeze: bool, conflicted: bool }, ... } ``` into ```rs enum PermissionPriv { ReservedFrz { conflicted: bool }, ReservedIM, ... } ``` but this is not the only solution and `Reserved(Activable | Conflicted | InteriorMut)` could be discussed. In addition to making the unreachable state not representable anymore, this change has the nice side effect of enabling `foreign_read` to no longer depend explicitly on the `protected` flag. Currently waiting for - `@JoJoDeveloping` to confirm that this is the same representation of `Reserved` as what is being implemented in simuliris, - `@RalfJung` to approve that this does not introduce too much overhead in the trusted codebase.
Make unused states of Reserved unrepresentable In the [previous TB update](rust-lang/miri#3742) we discovered that the existence of `Reserved + !ty_is_freeze + protected` is undesirable. This has the side effect of making `Reserved { conflicted: true, ty_is_freeze: false }` unreachable. As such it is desirable that this state would also be unrepresentable. This PR eliminates the unused configuration by changing ```rs enum PermissionPriv { Reserved { ty_is_freeze: bool, conflicted: bool }, ... } ``` into ```rs enum PermissionPriv { ReservedFrz { conflicted: bool }, ReservedIM, ... } ``` but this is not the only solution and `Reserved(Activable | Conflicted | InteriorMut)` could be discussed. In addition to making the unreachable state not representable anymore, this change has the nice side effect of enabling `foreign_read` to no longer depend explicitly on the `protected` flag. Currently waiting for - `@JoJoDeveloping` to confirm that this is the same representation of `Reserved` as what is being implemented in simuliris, - `@RalfJung` to approve that this does not introduce too much overhead in the trusted codebase.
Make unused states of Reserved unrepresentable In the [previous TB update](rust-lang/miri#3742) we discovered that the existence of `Reserved + !ty_is_freeze + protected` is undesirable. This has the side effect of making `Reserved { conflicted: true, ty_is_freeze: false }` unreachable. As such it is desirable that this state would also be unrepresentable. This PR eliminates the unused configuration by changing ```rs enum PermissionPriv { Reserved { ty_is_freeze: bool, conflicted: bool }, ... } ``` into ```rs enum PermissionPriv { ReservedFrz { conflicted: bool }, ReservedIM, ... } ``` but this is not the only solution and `Reserved(Activable | Conflicted | InteriorMut)` could be discussed. In addition to making the unreachable state not representable anymore, this change has the nice side effect of enabling `foreign_read` to no longer depend explicitly on the `protected` flag. Currently waiting for - `@JoJoDeveloping` to confirm that this is the same representation of `Reserved` as what is being implemented in simuliris, - `@RalfJung` to approve that this does not introduce too much overhead in the trusted codebase.
As discovered by @JoJoDeveloping, the result of having both Protector exceptions on lazy locations (protectors only protect initialized bytes) and interior mutability exceptions for protected tags (Reserved IM does not accept foreign writes when protected) leads to some very undesirable results, namely that we cannot do spurious writes even on protected activated locations.
We propose that Protected Reserved IM should no longer exist and instead when a type is retagged as part of a
FnEntry
it is assumed to lose interior mutability.In fact, this was already being done implicitly because relevant transitions were guarded by an
if protected
, but the difference is that now it also applies to transitions that occur after the end of the protector.