From 3aef80144ce0ab35d5cf7060e97cc79507731526 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sat, 18 May 2024 16:04:33 -0700 Subject: [PATCH] Add more safety proof to `KnownLayout` derive (#1302) Makes progress on #429 --- src/lib.rs | 20 ++++++-------------- zerocopy-derive/src/lib.rs | 31 ++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 084d5fe743..fbd838f125 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -785,24 +785,16 @@ unsafe impl 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() } } diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index c08ef8042f..f1a7f389a5 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -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,