Skip to content

Commit

Permalink
Add more safety proof to KnownLayout derive (#1302)
Browse files Browse the repository at this point in the history
Makes progress on #429
  • Loading branch information
joshlf committed May 18, 2024
1 parent d1d416d commit 3aef801
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 17 deletions.
20 changes: 6 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,24 +785,16 @@ unsafe impl<T> KnownLayout for [T] {
let slc = unsafe { &*slc };

// This is correct because the preceding `as` cast preserves the number
// of slice elements. Per
// https://doc.rust-lang.org/nightly/reference/expressions/operator-expr.html#slice-dst-pointer-to-pointer-cast:
// of slice elements. [1]
//
// [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast:
//
// For slice types like `[T]` and `[U]`, the raw pointer types `*const
// [T]`, `*mut [T]`, `*const [U]`, and `*mut [U]` encode the number of
// elements in this slice. Casts between these raw pointer types
// preserve the number of elements. Note that, as a consequence, such
// casts do *not* necessarily preserve the size of the pointer's
// referent (e.g., casting `*const [u16]` to `*const [u8]` will result
// in a raw pointer which refers to an object of half the size of the
// original). The same holds for `str` and any compound type whose
// unsized tail is a slice type, such as struct `Foo(i32, [u8])` or
// `(u64, Foo)`.
//
// TODO(#429),
// TODO(https://github.com/rust-lang/reference/pull/1417): Once this
// text is available on the Stable docs, cite those instead of the
// Nightly docs.
// preserve the number of elements. ... The same holds for `str` and
// any compound type whose unsized tail is a slice type, such as
// struct `Foo(i32, [u8])` or `(u64, Foo)`.
slc.len()
}
}
Expand Down
31 changes: 28 additions & 3 deletions zerocopy-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,34 @@ fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream {
};

// SAFETY:
// - The recursive call to `raw_from_ptr_len` preserves both address and provenance.
// - The `as` cast preserves both address and provenance.
// - `NonNull::new_unchecked` preserves both address and provenance.
// - The returned pointer has the same address and provenance as
// `bytes`:
// - The recursive call to `raw_from_ptr_len` preserves both
// address and provenance.
// - The `as` cast preserves both address and provenance.
// - `NonNull::new_unchecked` preserves both address and
// provenance.
// - If `Self` is a slice DST, the returned pointer encodes
// `elems` elements in the trailing slice:
// - This is true of the recursive call to `raw_from_ptr_len`.
// - `trailing.as_ptr() as *mut Self` preserves trailing slice
// element count [1].
// - `NonNull::new_unchecked` preserves trailing slice element
// count.
//
// [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast:
//
// `*const T`` / `*mut T` can be cast to `*const U` / `*mut U`
// with the following behavior:
// ...
// - If `T` and `U` are both unsized, the pointer is also
// returned unchanged. In particular, the metadata is
// preserved exactly.
//
// For instance, a cast from `*const [T]` to `*const [U]`
// preserves the number of elements. ... The same holds
// for str and any compound type whose unsized tail is a
// slice type, such as struct `Foo(i32, [u8])` or `(u64, Foo)`.
#[inline(always)]
fn raw_from_ptr_len(
bytes: ::zerocopy::macro_util::core_reexport::ptr::NonNull<u8>,
Expand Down

0 comments on commit 3aef801

Please sign in to comment.