Skip to content

Possible UB from Box<T> aliasing #74

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

Closed
nico-abram opened this issue Nov 22, 2020 · 7 comments · Fixed by #83
Closed

Possible UB from Box<T> aliasing #74

nico-abram opened this issue Nov 22, 2020 · 7 comments · Fixed by #83

Comments

@nico-abram
Copy link

nico-abram commented Nov 22, 2020

While watching yesterday's live stream Box shallow copying was mentioned, and me and a couple viewers asked about aliasing

I asked in the UFC zulip stream about it

Quoting RalfJ:

RalfJ: it's more that Box is an exclusive ptr and even reads 
        through aliases are disallowed when a ptr is exclusive

RalfJ: (this grants extra optimization power because it means 
        nobody else can even observe the current value stored behind the ptr)

Example which triggers UB according to MIRI (As provided by bjorn3 in the zulip topic)

Even just making a shared reference instead of actually reading the Copy value also triggers UB according to MIRI

Changing it to make the shallow copy the same way as the Box<T> shallow copy impl seems to change nothing: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6e7b08eb28d6032fbdb41028225f4dde

@nico-abram
Copy link
Author

Related comment #73 (comment)

@jonhoo
Copy link
Owner

jonhoo commented Nov 22, 2020

Thanks! Just for reference I'll add my reply to that to remind myself in the future:

My guess is that the best way to fix this is to move to NonNull everywhere I store a Box now.

It does have pretty unfortunate implications for ShallowCopy though, because it suggests that you can't ever ShallowCopy something that holds a Box either. That would be extremely unfortunate.

@danielhenrymantilla
Copy link

It does have pretty unfortunate implications for ShallowCopy though, because it suggests that you can't ever ShallowCopy something that holds a Box either. That would be extremely unfortunate.

What do you mean? Once you use a ptr::NonNull or any other kind of raw pointer, the aliasing guarantees on the pointee are inert, only being asserted on dereference.

And since having &Box<_>es is allowed in safe-code, having ptr::NonNull<Box<_>>s that are only &-dereferenced is fine too.

jonhoo added a commit that referenced this issue Nov 28, 2020
This partially addresses #74. There is still a violation in implementing
`ShallowCopy` for `Box`, but that one we'll tackle separately.
@jonhoo
Copy link
Owner

jonhoo commented Nov 28, 2020

@danielhenrymantilla My concern specifically is that it makes it very unergonomic to work with boxed values in evmap, since you cannot simply use a value that contains Box — you'd need to wrap it in some other type and then deal with the resulting unsafety in your own code. NonNull<Box<_>>, besides being a pointer to a pointer, is not very ergonomic, and requires unsafe annotations every time you use it even though those particular safety variants are all upheld by the library itself. The wrapper is just there to "nullify" the aliasing requirement of Box.

jonhoo added a commit that referenced this issue Nov 29, 2020
This is trying to address the unsoundness that arises from the current
version of `ShallowCopy` (see #74). In the process, it also deals with
the fact that casting between `Inner<.., T, ..>` and `Inner<..,
ManuallyDrop<T>, ..>` is likely not sound
(rust-lang/unsafe-code-guidelines#35 (comment)).
It does not yet work.
jonhoo added a commit that referenced this issue Nov 29, 2020
This is trying to address the unsoundness that arises from the current
version of `ShallowCopy` (see #74). In the process, it also deals with
the fact that casting between `Inner<.., T, ..>` and `Inner<..,
ManuallyDrop<T>, ..>` is likely not sound
(rust-lang/unsafe-code-guidelines#35 (comment)).
It does not yet work.
jonhoo added a commit that referenced this issue Nov 29, 2020
This is trying to address the unsoundness that arises from the current
version of `ShallowCopy` (see #74). In the process, it also deals with
the fact that casting between `Inner<.., T, ..>` and `Inner<..,
ManuallyDrop<T>, ..>` is likely not sound
(rust-lang/unsafe-code-guidelines#35 (comment)).
It does not yet work.
@jonhoo
Copy link
Owner

jonhoo commented Nov 29, 2020

Quick update: I believe that the use in left-right is now sound, since it no longer keeps aliased Boxes around as of d1b0477. I still haven't figured out what to do in evmap around ShallowCopy though. See #81 (comment) for an in-depth exploration of the problem.

@danielhenrymantilla
Copy link

@jonhoo I think I misunderstood your

something that holds a Box either

I interpreted it as "Box<T> -> ptr::NonNull<T> is not enough in case T = Box<U>" (double indirection already present; and in that case it is fine).

But if you are worried about a general P -> MaybeAliased<P> transformation, where P may be Box<T> or &mut T, etc., then I think MaybeUninit is the most appropriate wrapper for that right now, but I'm not 100% sure. I have commented on the associated UCG issue to discuss about that point.

jonhoo added a commit that referenced this issue Nov 29, 2020
This is trying to address the unsoundness that arises from the current
version of `ShallowCopy` (see #74). In the process, it also deals with
the fact that casting between `Inner<.., T, ..>` and `Inner<..,
ManuallyDrop<T>, ..>` is likely not sound
(rust-lang/unsafe-code-guidelines#35 (comment)).
It does not yet work.
@jonhoo
Copy link
Owner

jonhoo commented Nov 29, 2020

@danielhenrymantilla Yes, I agree with you, I think MaybeUninit is the way to go. Take a look at #83, and in particular https://github.com/jonhoo/rust-evmap/blob/4e83da000cfee94810eb790f25c1dc159def1b12/evmap/src/aliasing.rs!

jonhoo added a commit that referenced this issue Nov 29, 2020
This implementation gets rid of the unsound `ShallowCopy` trait (#74 &
rust-lang/unsafe-code-guidelines#35 (comment)),
and replaces it with a wrapper type around aliased values.

The core mechanism is that the wrapper type holds a `MaybeUninit<T>`,
and aliases it by doing a `ptr::read` of the whole `MaybeUninit<T>` to
alias. It then takes care to only give out `&T`s, which is allowed to
alias. To drop, the implementation sets a thread-local that the wrapper
uses to determine if it should truly drop the inner `T` (i.e., to
indicate that this is the last copy, and the `T` is no longer aliased).

The removal of `ShallowCopy` makes some of the API nicer, and obviates
the need for `evmap-derive`. Unfortunately the introduction of the
wrapper also complicates certain bounds which now need to know about the
wrapping. Overall though, an ergonomic win.

The implementation passes all tests, and I _think_ it is sound (if you
think it's not, please let me know!). The one bit that I'm not sure
about is the thread-local if a user nests `evmap`s (since they'll share
that thread local).

Note that this does _not_ take care of #78.

Fixes #74.
Fixes #55 since `ShallowCopy` is no longer neeeded.
jonhoo added a commit that referenced this issue Dec 6, 2020
This implementation gets rid of the unsound `ShallowCopy` trait (#74 &
rust-lang/unsafe-code-guidelines#35 (comment)),
and replaces it with a wrapper type around aliased values.

The core mechanism is that the wrapper type holds a `MaybeUninit<T>`,
and aliases it by doing a `ptr::read` of the whole `MaybeUninit<T>` to
alias. It then takes care to only give out `&T`s, which is allowed to
alias.

To drop, the implementation casts between two different generic
arguments of the new `Aliased` type, where one type causes the
`Aliased<T>` to drop the inner `T` and the other does not. The
documentation goes into more detail. The resulting cast _should_ be safe
(unlike the old `ManuallyDrop` cast).

The removal of `ShallowCopy` makes some of the API nicer by removing
trait bounds, and obviates the need for `evmap-derive`. While I was
going through, I also took the liberty of tidying up the external API of
`evmap` a bit.

The implementation passes all tests, and I _think_ it is sound (if you
think it's not, please let me know!).

Note that this does _not_ take care of #78.

Fixes #74.
Fixes #55 since `ShallowCopy` is no longer neeeded.
Also fixes #72 for the same reason.
@jonhoo jonhoo closed this as completed in 416ccef Dec 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants