diff --git a/crates/bevy_ecs/src/storage/blob_vec.rs b/crates/bevy_ecs/src/storage/blob_vec.rs index 384c26a07dc7a..8a223125d15e6 100644 --- a/crates/bevy_ecs/src/storage/blob_vec.rs +++ b/crates/bevy_ecs/src/storage/blob_vec.rs @@ -50,9 +50,10 @@ impl BlobVec { capacity: usize, ) -> BlobVec { if item_layout.size() == 0 { + let align = NonZeroUsize::new(item_layout.align()).expect("alignment must be > 0"); BlobVec { swap_scratch: NonNull::dangling(), - data: NonNull::dangling(), + data: bevy_ptr::dangling_with_align(align), capacity: usize::MAX, len: 0, item_layout, @@ -405,7 +406,8 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize { #[cfg(test)] mod tests { - use crate::ptr::OwningPtr; + use crate as bevy_ecs; // required for derive macros + use crate::{component::Component, ptr::OwningPtr, world::World}; use super::BlobVec; use std::{alloc::Layout, cell::RefCell, rc::Rc}; @@ -544,4 +546,28 @@ mod tests { // SAFETY: drop is able to drop a value of its `item_layout` let _ = unsafe { BlobVec::new(item_layout, Some(drop), 0) }; } + + #[test] + fn aligned_zst() { + // NOTE: This test is explicitly for uncovering potential UB with miri. + + #[derive(Component)] + #[repr(align(32))] + struct Zst; + + let mut world = World::default(); + world.spawn(Zst); + world.spawn(Zst); + world.spawn(Zst); + world.spawn_empty(); + + let mut count = 0; + + let mut q = world.query::<&Zst>(); + for &Zst in q.iter(&world) { + count += 1; + } + + assert_eq!(count, 3); + } } diff --git a/crates/bevy_ptr/src/lib.rs b/crates/bevy_ptr/src/lib.rs index 8f1f476a906e2..3107ab2d5ecc8 100644 --- a/crates/bevy_ptr/src/lib.rs +++ b/crates/bevy_ptr/src/lib.rs @@ -2,7 +2,9 @@ #![no_std] #![warn(missing_docs)] -use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ptr::NonNull}; +use core::{ + cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, num::NonZeroUsize, ptr::NonNull, +}; /// Type-erased borrow of some unknown type chosen when constructing this type. /// @@ -243,6 +245,14 @@ impl<'a, T> From<&'a [T]> for ThinSlicePtr<'a, T> { } } +/// Creates a dangling pointer with specified alignment. +/// See [`NonNull::dangling`]. +pub fn dangling_with_align(align: NonZeroUsize) -> NonNull { + // SAFETY: The pointer will not be null, since it was created + // from the address of a `NonZeroUsize`. + unsafe { NonNull::new_unchecked(align.get() as *mut u8) } +} + mod private { use core::cell::UnsafeCell;