-
-
Notifications
You must be signed in to change notification settings - Fork 17
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
Beef::owned_ptr is (probably) unsound as written #7
Comments
So if I understand it all correctly this should be a sufficient fix until #[inline]
unsafe fn owned_ptr(owned: Vec<T>) -> NonNull<[T]> {
let mut owned = mem::ManuallyDrop::new(owned);
let ptr = owned.as_mut_ptr();
let len = owned.len();
NonNull::new_unchecked(slice_from_raw_parts_mut(ptr, len))
} Edit: |
Given the current implementation of The main advantage of using |
Cool, also changing the trait to: pub unsafe trait Beef: ToOwned {
fn owned_into_parts(owned: Self::Owned) -> (NonNull<Self>, Option<NonZeroUsize>);
unsafe fn owned_from_parts(ptr: NonNull<Self>, capacity: NonZeroUsize) -> Self::Owned;
}
|
Looks like I missed the action. ;)
Yes. Passing a value to a function or returning it from a function is a "use" at the given type, such values must be valid for that type. Also see rust-lang/rust#69618: |
Makes sense, thanks for clarifying! |
because there is a semantic difference between
&mut self as *mut Self
andSelf::into_raw(self)
. The APIBeef
uses should at the very least be forwards compatible with being implemented in terms ofinto_raw_parts
, which will also help safeguard against misuse. I would suggest functions isomorphic tointo_raw(owned: Self::Owned) -> (NonNull<Self>, Option<NonZeroUsize>)
andfrom_raw(ptr: NonNull<Self>, cap: NonZeroUsize) -> Self::Owned
.To be fair, this is probably a lot more benign than the Rc case which used
&T as *const T
, but this is still problematic. IIUC, the actual UB under Stacked Borrows would come when dropping the owned value, as your pointer no longer actually owns the place it points to, and merely borrows it. Except...mem::forget
counts as a use of the pointer per SB, I think. cc @RalfJung if you don't mind?Roughly, and abusing the SB notation, here's the operations I think go on:
[Uniq]
&mut
ref taken to callowned_ptr
. Borrow stack:[Uniq, Mut]
&mut ref
is converted into a raw red. Borrow stack:[Uniq, Mut, Raw]
mem::forget
. This results in a fork, because I don't know the exact retagging semantics here.[Uniq]
, and any further use of the derived raw pointer is UB as it is no longer on the borrow stack. (I think this is the actual operational case (as opposed to abstract machine case) iff the owned pointer is markednoalias
for LLVM. This is why I'm abusing the borrow stack notation to distinguish the owned pointer from raw pointers.)[Uniq, Mut, Raw]
rebuild
. Borrow stack:[Uniq, Mut, Raw, Uniq]
Uniq
are never used.So I've talked myself from "(probably)" to "(potentially)", but I'd still advise changing the API surface forwards compatible with the definitely-not unsound
into_raw_parts
. At the very least, it avoids a misuse vector for the API.With apologies to the Stacked Borrow team for my abuse of their notation above.
The text was updated successfully, but these errors were encountered: