Is mutating through a `& *mut T` valid?

In the wasi crate, I noticed fd_read takes a &[IoVec] (IoVec is basically a *mut [u8]) and writes into it from the given file descriptor.

No mutable reference is created, and the mutation is done by an extern fn call, but this still seems odd to me, as it looks like a way to perform mutation behind a shared reference outside of UnsafeCell.
Does the fact that it's a raw pointer rather than a reference make it okay?

Yes, it's valid. Raw pointers are Copy types. Which means in particular that you can get back a *mut T from a &*mut T, and then use that *mut T.

3 Likes

Creating a mutable reference would be valid, too. (Provided the pointer is valid for reading+writing and only one mutable reference is created and used at a time across all copies of the same pointer. And many other ways of using the pointer may not overlap/interleave with the active lifetime of such a mutable reference either.)

There are lots of similarities between *mut T and &UnsafeCell<T>; both allow shared mutable access.

I think it's tricky to give a real solid answer here without a lot of caveats. The answer to the question in the subject is "it's valid", if we

  • Note that you don't actually go "through" the outer reference, as you can copy the *mut out, like @steffahn said
  • Take "valid" to mean "possible to perform without causing UB". Mutating through the *mut T itself is an unsafe operation, which may or may not be UB.

And thus we can say that the example in the body of the OP is "okay" in that

  • It's definitely sound in that you can't cause UB with fd_read without unsafe
    • But this is a vacuous observation since it's an unsafe fn
  • But it's also possible to use without causing UB
    • At least as far as we can tell from the signature alone, as any further safety invariants do not seem to be documented on the function
2 Likes

Indeed. Raw pointers basically are another kind of interior mutability primitive. So as long as you don't create overlapping &muts out of them, mutation through a *mut in itself should not be unsound.

What determines whether it's safe to write through a raw pointer is how the raw pointer was originally created - things that just move it around (including copying or casting it) do not change what you can do with a particular raw pointer.

The reason that UnsafeCell exists even though raw pointers can do the same thing is that an UnsafeCell works without a pointer-indirection, whereas a raw pointer requires, well, that you have a pointer.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.