Skip to content

Commit d3569dd

Browse files
authored
Rollup merge of rust-lang#62323 - Centril:clarify-read-unaligned, r=RalfJung
Clarify unaligned fields in ptr::{read,write}_unaligned r? @RalfJung
2 parents 84527e4 + 54527db commit d3569dd

File tree

1 file changed

+53
-35
lines changed

1 file changed

+53
-35
lines changed

src/libcore/ptr/mod.rs

+53-35
Original file line numberDiff line numberDiff line change
@@ -625,42 +625,50 @@ pub unsafe fn read<T>(src: *const T) -> T {
625625
/// [read-ownership]: ./fn.read.html#ownership-of-the-returned-value
626626
/// [valid]: ../ptr/index.html#safety
627627
///
628-
/// # Examples
628+
/// ## On `packed` structs
629629
///
630-
/// Access members of a packed struct by reference:
630+
/// It is currently impossible to create raw pointers to unaligned fields
631+
/// of a packed struct.
631632
///
632-
/// ```
633-
/// use std::ptr;
633+
/// Attempting to create a raw pointer to an `unaligned` struct field with
634+
/// an expression such as `&packed.unaligned as *const FieldType` creates an
635+
/// intermediate unaligned reference before converting that to a raw pointer.
636+
/// That this reference is temporary and immediately cast is inconsequential
637+
/// as the compiler always expects references to be properly aligned.
638+
/// As a result, using `&packed.unaligned as *const FieldType` causes immediate
639+
/// *undefined behavior* in your program.
634640
///
641+
/// An example of what not to do and how this relates to `read_unaligned` is:
642+
///
643+
/// ```no_run
635644
/// #[repr(packed, C)]
636645
/// struct Packed {
637646
/// _padding: u8,
638647
/// unaligned: u32,
639648
/// }
640649
///
641-
/// let x = Packed {
650+
/// let packed = Packed {
642651
/// _padding: 0x00,
643652
/// unaligned: 0x01020304,
644653
/// };
645654
///
646655
/// let v = unsafe {
647-
/// // Take the address of a 32-bit integer which is not aligned.
648-
/// // This must be done as a raw pointer; unaligned references are invalid.
649-
/// let unaligned = &x.unaligned as *const u32;
650-
///
651-
/// // Dereferencing normally will emit an aligned load instruction,
652-
/// // causing undefined behavior.
653-
/// // let v = *unaligned; // ERROR
656+
/// // Here we attempt to take the address of a 32-bit integer which is not aligned.
657+
/// let unaligned =
658+
/// // A temporary unaligned reference is created here which results in
659+
/// // undefined behavior regardless of whether the reference is used or not.
660+
/// &packed.unaligned
661+
/// // Casting to a raw pointer doesn't help; the mistake already happened.
662+
/// as *const u32;
654663
///
655-
/// // Instead, use `read_unaligned` to read improperly aligned values.
656-
/// let v = ptr::read_unaligned(unaligned);
664+
/// let v = std::ptr::read_unaligned(unaligned);
657665
///
658666
/// v
659667
/// };
660-
///
661-
/// // Accessing unaligned values directly is safe.
662-
/// assert!(x.unaligned == v);
663668
/// ```
669+
///
670+
/// Accessing unaligned fields directly with e.g. `packed.unaligned` is safe however.
671+
// FIXME: Update docs based on outcome of RFC #2582 and friends.
664672
#[inline]
665673
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
666674
pub unsafe fn read_unaligned<T>(src: *const T) -> T {
@@ -789,38 +797,48 @@ pub unsafe fn write<T>(dst: *mut T, src: T) {
789797
///
790798
/// [valid]: ../ptr/index.html#safety
791799
///
792-
/// # Examples
800+
/// ## On `packed` structs
793801
///
794-
/// Access fields in a packed struct:
802+
/// It is currently impossible to create raw pointers to unaligned fields
803+
/// of a packed struct.
795804
///
796-
/// ```
797-
/// use std::{mem, ptr};
805+
/// Attempting to create a raw pointer to an `unaligned` struct field with
806+
/// an expression such as `&packed.unaligned as *const FieldType` creates an
807+
/// intermediate unaligned reference before converting that to a raw pointer.
808+
/// That this reference is temporary and immediately cast is inconsequential
809+
/// as the compiler always expects references to be properly aligned.
810+
/// As a result, using `&packed.unaligned as *const FieldType` causes immediate
811+
/// *undefined behavior* in your program.
798812
///
813+
/// An example of what not to do and how this relates to `write_unaligned` is:
814+
///
815+
/// ```no_run
799816
/// #[repr(packed, C)]
800-
/// #[derive(Default)]
801817
/// struct Packed {
802818
/// _padding: u8,
803819
/// unaligned: u32,
804820
/// }
805821
///
806822
/// let v = 0x01020304;
807-
/// let mut x: Packed = unsafe { mem::zeroed() };
808-
///
809-
/// unsafe {
810-
/// // Take a reference to a 32-bit integer which is not aligned.
811-
/// let unaligned = &mut x.unaligned as *mut u32;
823+
/// let mut packed: Packed = unsafe { std::mem::zeroed() };
812824
///
813-
/// // Dereferencing normally will emit an aligned store instruction,
814-
/// // causing undefined behavior because the pointer is not aligned.
815-
/// // *unaligned = v; // ERROR
825+
/// let v = unsafe {
826+
/// // Here we attempt to take the address of a 32-bit integer which is not aligned.
827+
/// let unaligned =
828+
/// // A temporary unaligned reference is created here which results in
829+
/// // undefined behavior regardless of whether the reference is used or not.
830+
/// &mut packed.unaligned
831+
/// // Casting to a raw pointer doesn't help; the mistake already happened.
832+
/// as *mut u32;
816833
///
817-
/// // Instead, use `write_unaligned` to write improperly aligned values.
818-
/// ptr::write_unaligned(unaligned, v);
819-
/// }
834+
/// std::ptr::write_unaligned(unaligned, v);
820835
///
821-
/// // Accessing unaligned values directly is safe.
822-
/// assert!(x.unaligned == v);
836+
/// v
837+
/// };
823838
/// ```
839+
///
840+
/// Accessing unaligned fields directly with e.g. `packed.unaligned` is safe however.
841+
// FIXME: Update docs based on outcome of RFC #2582 and friends.
824842
#[inline]
825843
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
826844
pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {

0 commit comments

Comments
 (0)