Skip to content

Commit

Permalink
Fix assembling mutable slice from const reference
Browse files Browse the repository at this point in the history
The pointer from which a mutable reference is created must have
provenance for accessing the underlying elements of the struct and its
fields (a single array here) mutably. A pointer acquired by
slice::as_ptr does not have that provenance, it only allows shared read
access. The `slice::as_ptr` method says:

> The caller must also ensure that the memory the pointer
(non-transitively) points to is never written to (except inside an
UnsafeCell) using this pointer or any pointer derived from it. If you
need to mutate the contents of the slice, use as_mut_ptr.

This fails MIRI which checks for this assertion but I'm not aware of
mis-compilation resulting from it. In fact, consider:

    pub fn as_ptr(slice: &mut [u8]) -> *const u8 {
        slice.as_ptr()
    }
    pub fn as_mut_ptr(slice: &mut [u8]) -> *mut u8 {
        slice.as_mut_ptr()
    }

<https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=28d3c19800236ae520441a22a6f99b19>

Then llvm-IR in optimization will show that the functions are collapsed
to a single one, hinting there is no semantic difference at that level.
The difference in codegen is a readonly attribute on the slice::as_ptr
argument but this is scoped to the particular function and argument, not
the pointer value itself:

> On a function, this attribute indicates that the function does not
write through any pointer arguments
<https://llvm.org/docs/LangRef.html>

Since the _outer_ function correctly takes a `&mut self` argument the
restriction possibly no longer applies to our own method. Of course, a
stricter codegen of Rust's memory model might not be as kind and exploit
the MIR level UB here.
  • Loading branch information
HeroicKatora committed Nov 12, 2020
1 parent 1ec76c9 commit 5cbe1e6
Showing 1 changed file with 1 addition and 1 deletion.
2 changes: 1 addition & 1 deletion src/color.rs
Expand Up @@ -253,7 +253,7 @@ impl<T: Primitive + 'static> Pixel for $ident<T> {
}
fn from_slice_mut(slice: &mut [T]) -> &mut $ident<T> {
assert_eq!(slice.len(), $channels);
unsafe { &mut *(slice.as_ptr() as *mut $ident<T>) }
unsafe { &mut *(slice.as_mut_ptr() as *mut $ident<T>) }
}

fn to_rgb(&self) -> Rgb<T> {
Expand Down

0 comments on commit 5cbe1e6

Please sign in to comment.