From efc46c3c34913b16a60571a0fcfe7a27c3875127 Mon Sep 17 00:00:00 2001 From: enthropy7 <221884178+enthropy7@users.noreply.github.com> Date: Thu, 13 Aug 2026 00:20:44 +0300 Subject: [PATCH] [layout] Restrict CastFrom's CastExact impl CastFrom preserves unpadded size, but lowering the alignment of a dynamically padded DST can shrink its referent. Require IntoBytes on the source so CastExact is only implemented for padding-free source layouts. Add a regression test using an aligned slice DST whose one-byte payload projects from an eight-byte referent to a one-byte slice. --- zerocopy/src/layout.rs | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/zerocopy/src/layout.rs b/zerocopy/src/layout.rs index d58786d637..36f9b5dc85 100644 --- a/zerocopy/src/layout.rs +++ b/zerocopy/src/layout.rs @@ -772,11 +772,14 @@ mod cast_from { { } - // SAFETY: The implementation of `Project::project` preserves the size of - // the referent (see inline comments for a more detailed proof of this). + // SAFETY: `Src: IntoBytes` guarantees that `Src` has no padding, so its + // unpadded size is a multiple of its alignment. `Project::project` + // preserves that size and requires `Src`'s alignment to be at least + // `Dst`'s. Thus `Dst` has no trailing padding and both referents have the + // same size. unsafe impl crate::pointer::cast::CastExact for CastFrom where - Src: KnownLayout + ?Sized, + Src: IntoBytes + KnownLayout + ?Sized, Dst: KnownLayout + ?Sized, { } @@ -1103,6 +1106,30 @@ mod cast_from { #[cfg(test)] mod tests { use super::*; + use crate::{KnownLayout, PtrInner}; + + #[test] + fn test_cast_from_may_shrink_dynamic_padding() { + #[derive(KnownLayout)] + #[repr(C, align(8))] + struct Aligned { + trailing: T, + } + + let src = Aligned { trailing: [0u8] }; + let src: &Aligned<[u8]> = &src; + let dst = PtrInner::from_ref(src).project::<[u8], CastFrom<[u8]>>(); + + assert_eq!(mem::size_of_val(src), 8); + assert_eq!(<[u8] as KnownLayout>::size_of_val_raw(dst.as_non_null()), Some(1)); + + static_assertions::assert_impl_all!( + CastFrom<[u8]>: crate::pointer::cast::Cast, [u8]> + ); + static_assertions::assert_not_impl_any!( + CastFrom<[u8]>: crate::pointer::cast::CastExact, [u8]> + ); + } #[test] fn test_dst_layout_for_slice() {