Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify unaligned fields in ptr::{read,write}_unaligned #62323

Merged
merged 2 commits into from
Jul 5, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 53 additions & 35 deletions src/libcore/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,42 +647,50 @@ pub unsafe fn read<T>(src: *const T) -> T {
/// [read-ownership]: ./fn.read.html#ownership-of-the-returned-value
/// [valid]: ../ptr/index.html#safety
///
/// # Examples
Centril marked this conversation as resolved.
Show resolved Hide resolved
/// ## On `packed` structs
///
/// Access members of a packed struct by reference:
/// It is currently impossible to create raw pointers to unaligned fields
/// of a packed struct.
///
/// ```
/// use std::ptr;
/// Attempting to create a raw pointer to an `unaligned` struct field with
/// an expression such as `&packed.unaligned as *const FieldType` creates an
/// intermediate unaligned reference before converting that to a raw pointer.
/// That this reference is temporary and immediately cast is inconsequential
/// as the compiler always expects references to be properly aligned.
/// As a result, using `&packed.unaligned as *const FieldType` causes immediate
/// *undefined behavior* in your program.
///
/// An example of what not to do and how this relates to `read_unaligned` is:
Centril marked this conversation as resolved.
Show resolved Hide resolved
///
/// ```no_run
/// #[repr(packed, C)]
/// struct Packed {
/// _padding: u8,
/// unaligned: u32,
/// }
///
/// let x = Packed {
/// let packed = Packed {
/// _padding: 0x00,
/// unaligned: 0x01020304,
/// };
///
/// let v = unsafe {
/// // Take the address of a 32-bit integer which is not aligned.
/// // This must be done as a raw pointer; unaligned references are invalid.
/// let unaligned = &x.unaligned as *const u32;
///
/// // Dereferencing normally will emit an aligned load instruction,
/// // causing undefined behavior.
/// // let v = *unaligned; // ERROR
/// // Here we attempt to take the address of a 32-bit integer which is not aligned.
/// let unaligned =
/// // A temporary unaligned reference is created here which results in
/// // undefined behavior regardless of whether the reference is used or not.
/// &packed.unaligned
/// // Casting to a raw pointer doesn't help; the mistake already happened.
/// as *const u32;
///
/// // Instead, use `read_unaligned` to read improperly aligned values.
/// let v = ptr::read_unaligned(unaligned);
/// let v = std::ptr::read_unaligned(unaligned);
///
/// v
/// };
///
/// // Accessing unaligned values directly is safe.
/// assert!(x.unaligned == v);
/// ```
///
/// Accessing unaligned fields directly with e.g. `packed.unaligned` is safe however.
// FIXME: Update docs based on outcome of RFC #2582 and friends.
#[inline]
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
pub unsafe fn read_unaligned<T>(src: *const T) -> T {
Expand Down Expand Up @@ -811,38 +819,48 @@ pub unsafe fn write<T>(dst: *mut T, src: T) {
///
/// [valid]: ../ptr/index.html#safety
///
/// # Examples
/// ## On `packed` structs
///
/// Access fields in a packed struct:
/// It is currently impossible to create raw pointers to unaligned fields
/// of a packed struct.
///
/// ```
/// use std::{mem, ptr};
/// Attempting to create a raw pointer to an `unaligned` struct field with
/// an expression such as `&packed.unaligned as *const FieldType` creates an
/// intermediate unaligned reference before converting that to a raw pointer.
/// That this reference is temporary and immediately cast is inconsequential
/// as the compiler always expects references to be properly aligned.
/// As a result, using `&packed.unaligned as *const FieldType` causes immediate
/// *undefined behavior* in your program.
///
/// An example of what not to do and how this relates to `write_unaligned` is:
///
/// ```no_run
/// #[repr(packed, C)]
/// #[derive(Default)]
/// struct Packed {
/// _padding: u8,
/// unaligned: u32,
/// }
///
/// let v = 0x01020304;
/// let mut x: Packed = unsafe { mem::zeroed() };
///
/// unsafe {
/// // Take a reference to a 32-bit integer which is not aligned.
/// let unaligned = &mut x.unaligned as *mut u32;
/// let mut packed: Packed = unsafe { std::mem::zeroed() };
///
/// // Dereferencing normally will emit an aligned store instruction,
/// // causing undefined behavior because the pointer is not aligned.
/// // *unaligned = v; // ERROR
/// let v = unsafe {
/// // Here we attempt to take the address of a 32-bit integer which is not aligned.
/// let unaligned =
/// // A temporary unaligned reference is created here which results in
/// // undefined behavior regardless of whether the reference is used or not.
/// &mut packed.unaligned
/// // Casting to a raw pointer doesn't help; the mistake already happened.
/// as *mut u32;
///
/// // Instead, use `write_unaligned` to write improperly aligned values.
/// ptr::write_unaligned(unaligned, v);
/// }
/// std::ptr::write_unaligned(unaligned, v);
///
/// // Accessing unaligned values directly is safe.
/// assert!(x.unaligned == v);
/// v
/// };
/// ```
///
/// Accessing unaligned fields directly with e.g. `packed.unaligned` is safe however.
// FIXME: Update docs based on outcome of RFC #2582 and friends.
#[inline]
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
Expand Down