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

Add more safety proof to KnownLayout derive #1302

Merged
merged 1 commit into from
May 18, 2024
Merged
Show file tree
Hide file tree
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
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
Loading