Skip to content

Unsoundness in OwningRef::map_with_owner and more #77

@noamtashma

Description

@noamtashma

I found more unsoundness problems. This extends #61 .

In particular, OwningRef::map_with_owner allows creating an OwningRef that points at the owner, which can be moved.

fn unstable_address() {
    let ow_ref = OwningRef::new(Box::new(5));
    let new_ow_ref = ow_ref.map_with_owner(|owner, _my_ref| owner);
    println!("Reading memory that was moved from: {}", *new_ow_ref);
}

This by itself can be fixed by replacing map_with_owner with a method that only gives out a reference to the referent of the owner, like this:

pub fn map_with_owner<F, U: ?Sized>(self, f: F) -> OwningRef<'t, O, U>
    where O: StableAddress + Deref,
        F: for<'a> FnOnce(&'a O::Target, &'a T) -> &'a U

In addition, There's unsoundness combining a conversion from OwningRefMut to OwningRef together with methods that can read the owner of an OwningRef, like so:

fn ref_mut_to_ref() {
    use core::cell::RefCell; // `Cell` and any other kind of cell also works
    let ow_ref = OwningRefMut::new(Box::new(RefCell::new(Box::new(5))));
    let new_ow_ref = ow_ref.map(|cell| &**cell.get_mut());
    // Have to convert to `OwningRef` In order to use `OwningRef::as_owner`
    // instead of `OwningRefMut::as_owner`.
    *new_ow_ref.as_owner().borrow_mut() = Box::new(9);
    println!("Reading deallocated memory: {}", *new_ow_ref);
}

OwningRefMut::{as_owner, as_owner_mut} can also be used, as #61 shows.

There are two ways to fix this, and each choice corresponds to a small difference in the meaning and invariants of OwningRef:

  • Disallow conversions from OwningRefMut to OwningRef, and allow shared access to the owner of an OwningRef.
    Invariant: the reference of the OwningRef may only borrow immutably from the owner.
  • Allow conversions from OwningRefMut to OwningRef, but disallow shared access to the owner of an OwningRef.
    Invariant: the reference of the OwningRef may borrow immutably or mutably from the owner.

Essentially, these are two distinct types, which are both sound by themselves, and a third option is to have both types.

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