You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
When using std::ptr::copy and similar methods, does the memory need to be valid for T? #330
Plenty of methods in std::ptr take a generic T and use *mut/const T, but does this mean that the following code is UB?
let src:u8 = 42;letmut dest:u8 = 10;
std::ptr::copy(&src as*constu8as*constbool,&mut dest as*mutu8as*mutbool);
What about copying a u32 as a (u16, u8), or a (u16, u8, !), that doesn't uninit any padding, right?
Same question applies to copy_nonoverlapping, swap(_nonoverlapping), write_bytes. I don't see any other methods that this would apply to.
What about copying a pointer with an integer type, that preserves provenance?
I'd be inclined to say "no, it's not UB, the generic parameter is only used for its size and alignment, no other property of the type matters", but if that's the case, we should make that clear. It definitely seems to be implied.
It's not UB necessarily, but it is semantically different. The implementation is permitted to not copy padding bytes and/or make them uninit in the destination if you copy a u32 as a (u16, u8), so reading the destination at u32 later will most likely be UB.
Relevant discussion: rust-lang/rust#63159 . The resolution of that issue will determine whether copy and copy_nonoverlapping copy padding bytes or do a typed copy.
About the UB issue, AFAIU the whole point of raw pointers operations (ie ptr.write(val) instead of *ptr = val) is to not assert validity, meaning that this code is valid AFAIU:
because we never created a reference to u8 nor did we read its value, so it should be valid code.
but as @digama0 said, the type is used for more than just size+align, I read this code as "Copy the values like you would with *ptr = value; without asserting the validity of the value", so when you copy a bool it could just copy a single bit and ignore the rest, just like with a regular bool write.
@elichai That's not my impression, although you might be right in the specific case of ptr::copy as discussed in the linked issue. For ptr::write and ptr::read though I'm pretty sure these must be typed copies because the type signature of the function includes an explicit value of type T which is typed-copied from the callee's return place to the caller's temporary storage (at least semantically, even if this is optimized away in practice). So something like MaybeUninit::<u32>::uninit().as_ptr().read() looks like it should be UB (I think it is still open whether to do this for u32 but if you use bool instead then this is definitely UB), even if the value is unused.
Edit: Miri will currently report UB if you use ptr::write or ptr::read with uninit bool but not on u32. It will not complain if you ptr::copy an uninit bool.
Edit: Miri will currently report UB if you use ptr::write or ptr::read with uninit bool but not on u32. It will not complain if you ptr::copy an uninit bool.
Well, to use ptr::write on uninitialized data you need to first read it into the stack as T not as MaybeUninit<T>, so that by itself is UB.
It seems like ptr::copy* are the only functions that allow you to write uninit data without first reading it.
As was already noted, this is basically the same as rust-lang/rust#63159: do these operations do a typed copy (like a read followed by a write), or do they propagate the underlying bytes unchanged and T basically doesn't matter (except for its type)?
For backwards compatibility, we probably have to go with the latter here.
Activity
digama0 commentedon Apr 24, 2022
It's not UB necessarily, but it is semantically different. The implementation is permitted to not copy padding bytes and/or make them uninit in the destination if you copy a u32 as a
(u16, u8), so reading the destination atu32later will most likely be UB.Relevant discussion: rust-lang/rust#63159 . The resolution of that issue will determine whether
copyandcopy_nonoverlappingcopy padding bytes or do a typed copy.elichai commentedon Apr 24, 2022
About the UB issue, AFAIU the whole point of raw pointers operations (ie
ptr.write(val)instead of*ptr = val) is to not assert validity, meaning that this code is valid AFAIU:because we never created a reference to
u8nor did we read its value, so it should be valid code.but as @digama0 said, the type is used for more than just size+align, I read this code as "Copy the values like you would with *ptr = value; without asserting the validity of the value", so when you copy a bool it could just copy a single bit and ignore the rest, just like with a regular bool write.
digama0 commentedon Apr 24, 2022
@elichai That's not my impression, although you might be right in the specific case of
ptr::copyas discussed in the linked issue. Forptr::writeandptr::readthough I'm pretty sure these must be typed copies because the type signature of the function includes an explicit value of typeTwhich is typed-copied from the callee's return place to the caller's temporary storage (at least semantically, even if this is optimized away in practice). So something likeMaybeUninit::<u32>::uninit().as_ptr().read()looks like it should be UB (I think it is still open whether to do this foru32but if you useboolinstead then this is definitely UB), even if the value is unused.Edit: Miri will currently report UB if you use
ptr::writeorptr::readwith uninitboolbut not onu32. It will not complain if youptr::copyan uninitbool.elichai commentedon Apr 26, 2022
Well, to use
ptr::writeon uninitialized data you need to first read it into the stack asTnot asMaybeUninit<T>, so that by itself is UB.It seems like
ptr::copy*are the only functions that allow you to write uninit data without first reading it.RalfJung commentedon Apr 30, 2022
As was already noted, this is basically the same as rust-lang/rust#63159: do these operations do a typed copy (like a
readfollowed by awrite), or do they propagate the underlying bytes unchanged andTbasically doesn't matter (except for its type)?For backwards compatibility, we probably have to go with the latter here.
RalfJung commentedon Apr 30, 2022
With
-Zmiri-check-number-validityit will also complain onu32.👍
RalfJung commentedon Jul 8, 2022
Oh, good catch. Yes as far as I am concerned that was always meant to just mean "of course you can't use a [42_u8] as a bool".
Rollup merge of rust-lang#99084 - RalfJung:write_bytes, r=thomcc
Rollup merge of rust-lang#99084 - RalfJung:write_bytes, r=thomcc
Rollup merge of #99084 - RalfJung:write_bytes, r=thomcc