Apologies in advance if this is an RTFM question.
I'm trying to copy some memory within the same buffer using core::ptr::copy. miri doesn't like the code below. It doesn't seem to matter how far apart I put the src and dst pointers. (I can even put them 8KB apart) What do I need to do to make this not UB?
use core::mem::MaybeUninit;
fn main() {
let mut bytes: [MaybeUninit<u8>; 16] = [MaybeUninit::uninit(); 16];
for i in 0..16 {
bytes[i] = MaybeUninit::new(i as u8);
}
unsafe {
let src_ptr: *const u8 = bytes.as_ptr().cast();
let dst_ptr = bytes.as_mut_ptr().cast::<u8>().add(8);
core::ptr::copy(src_ptr, dst_ptr, 8);
}
for i in 0..16 {
println!("{}", unsafe{ bytes[i].assume_init()});
}
}
Thank you.
1 Like
The call to as_mut_ptr
requires a &mut
(exclusivity) that clobbers src_ptr
's "right" to access the memory.
Create a single source of mutable access and make all your pointers off of that.
let ptr = bytes.as_mut_ptr();
let src_ptr = ptr.cast_const();
let dst_ptr = ptr.add(8);
core::ptr::copy(src_ptr, dst_ptr, 8);
Alternatively, split things up in safe land ahead of time.
let (src, dst) = bytes.split_at_mut(8);
let src_ptr = src.as_ptr();
let dst_ptr = dst.as_mut_ptr();
unsafe {
core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, 8);
}
4 Likes
Thanks for that explanation.
Is it a matter of miri using a certain method to track pointers? Or is my original implementation actually UB?
Thanks again.
Yes and yes.
Rust's exact memory model hasn't been decided, but it will probably be some mix of stacked borrows and tree borrows at the extreme. Until it's decided, the only prudent approach is to stay within the intersection of both, at least. Last I checked Miri defaulted to tracking stacked borrows.
3 Likes