Soundness of a hypothetical OwningMut type

Inspired by OwningRef, I was thinking about whether an OwningMut type would be sound. A potential soundness hole occurred to me, and I'm not sure where the blame should go.

For completeness, here are the operations that could cause a problem, where T: ?Sized:

fn new(o: O) -> Self 
where O: StableAddress, O: DerefMut<Target=T>

fn map<F, U: ?Sized>(self, f: F) -> OwningMut<O, U> 
where O: StableAddress, F: FnOnce(&mut T) -> &mut U

fn owner(&self) -> &O
fn deref(&self) -> &T 

(StableAddress just ensures that moving the owning value doesn't invalidate references into it created by calling <O as DerefMut>::deref_mut.)

The conflict is with a hypothetical method on Cell:

fn deref_mut(&mut self) -> &mut T

This would allow you to do:

let mut v = OwningMut::new(Box::new(Cell::new(Some(3))));
let mut r = v.map(|cell| cell.deref_mut().as_mut().unwrap());
r.owner().set(None);
r.map(|invalid_reference| ...); // oops!

The problem is coming from an interaction between incompatible assumptions. The hypothetical Cell::deref_mut method assumes that the mutable reference it gives out must be permanently inaccessible by the time Cell::set can be called. Freezing it temporarily is insufficient. On the other hand, OwningMut::owner assumes that accessing a value through a shared reference cannot invalidate any references into it, however derived. Both assumptions can't be true, so I'm wondering which, if either, is sound.

If OwningMut::owner is unsound, would it be sound if there were a NoInteriorMutability bound to O?

The hypothetical Cell::deref_mut method assumes that the mutable reference it gives out must be permanently inaccessible by the time Cell::set can be called.

This seems like a reasonable assumption. I don't think it can be violated by safe Rust code. The assumption only fails here because OwningRef keeps a mutable borrow alive while also giving out shared references to the same memory, in violation of Rust rules.

On the other hand, OwningMut::owner assumes that accessing a value through a shared reference cannot invalidate any references into it, however derived.

This one seems unsound, since as you demonstrated, it's easy to violate with interior mutability.

If OwningMut::owner is unsound, would it be sound if there were a NoInteriorMutability bound to O?

I can't think of any remaining problems, but I don't know whether that's just a failure of imagination on my part. :slight_smile:

1 Like

By the way, there is an open PR to add such a method to Cell:

https://github.com/rust-lang/rust/pull/32565

1 Like