Skip to content

Beef::owned_ptr is (probably) unsound as written #7

@CAD97

Description

@CAD97

because there is a semantic difference between &mut self as *mut Self and Self::into_raw(self). The API Beef uses should at the very least be forwards compatible with being implemented in terms of into_raw_parts, which will also help safeguard against misuse. I would suggest functions isomorphic to into_raw(owned: Self::Owned) -> (NonNull<Self>, Option<NonZeroUsize>) and from_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:

  • Owned value created. Borrow stack: [Uniq]
  • Owned value has &mut ref taken to call owned_ptr. Borrow stack: [Uniq, Mut]
  • &mut ref is converted into a raw red. Borrow stack: [Uniq, Mut, Raw]
  • Owned value is passed to mem::forget. This results in a fork, because I don't know the exact retagging semantics here.
    • Case 1) this retags the unique pointer held by the owned value (i.e. "use"s the pointer). This pops the borrow stack to [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 marked noalias for LLVM. This is why I'm abusing the borrow stack notation to distinguish the owned pointer from raw pointers.)
    • Case 2) this does not retags the unique pointer. Continue below with no UB yet. Borrow stack: [Uniq, Mut, Raw]
  • Raw pointer is reconstructed to an owned pointer in rebuild. Borrow stack: [Uniq, Mut, Raw, Uniq]
  • Operation is probably UB free, as all pointers beneath the new 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions