Rc and internal mutability

Well, at least Stacked Borrows won't like that code. (Currently there are likely enough raw pointers around to camouflage the fault, but once we make a better effort at tracking those, I think this code would be ruled out). This reminds me a bit of container_of's unsoundess, though I think your code here can be fixed if you find a better way to create RcBorrow -- something that's actually meant to be used with from_raw.

But this makes me a bit worried about how much other code like this is out there, that might be incompatible with any attempt at properly restricting the aliasing of references.

That is not at all a given truth. We could decide for this, but it will severely limit optimizations. Being able to make assumptions even about "dead" values is extremely useful for a compiler.

3 Likes

After I get Arc::into_raw fixed I'm planning to add an Arc::as_raw and use that for ArcBorrow, so at least that resource will be correct. (Temporarily, I should try and see if I can get a clone+into_raw+drop to optimize away the ref count manipulation to sidestep the issue until as_raw is stable. I'll probably publish the crate as soon as I do that.)

But yes, "reverse pointer projection" is used in the wild by my experience. Typically, it involves raw pointers for at-rest storage, but because we don't have &raw, the "forward projection" always requires going through references (and pointer offsetting in the "forward" direction feels unneeded at first approximation).

And any type that uses a pointer not to the start of the managed allocation is at risk of falling afoul of something like this.

Now the rest of this post is musing on the subject:

  • Restricting arbitrary movement of &mut within an allocation makes complete and total sense. It has never even slightly been legal to go from &mut struct.field to &mut struct.
  • But what about shared references? That's a lot murkier: if you know that the outer bit is shared, why can't you access it?
  • The problem is when internal mutability comes into the the picture. Even with UnsafeCell, going from an internal reference to an external reference is definitely unsound.
  • People who want to do tricky things with unsafe do typically stick to working with raw pointers (or wrappers thereof) rather than try to lie with at-rest references. But temporarily manifesting references is hard to avoid. (And that temporarily may be in a local variable...)
  • So the solution seems to be make sticking with raw pointers and avoiding references easier, as quickly as possible.
  • (How expensive would a miri cargobomb be? Probably prohibitive, even if "impossible" crates (e.g. ffi) were filtered out.)

EDIT: tried to write a "correct" version of Arc::as_raw in user code. As it turns out, even a simple drop(arc.clone()) cannot be optimized out, likely due to atomic operation requirements. Rc seems to almost but not quite optimize out completely.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.