Skip to content

Commit 3ac06b5

Browse files
committed
Respect alignment for zero-sized types stored in the world (#6618)
# Objective Fixes #6615. `BlobVec` does not respect alignment for zero-sized types, which results in UB whenever a ZST with alignment other than 1 is used in the world. ## Solution Add the fn `bevy_ptr::dangling_with_align`. --- ## Changelog + Added the function `dangling_with_align` to `bevy_ptr`, which creates a well-aligned dangling pointer to a type whose alignment is not known at compile time.
1 parent 9498bff commit 3ac06b5

File tree

2 files changed

+39
-3
lines changed

2 files changed

+39
-3
lines changed

crates/bevy_ecs/src/storage/blob_vec.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ impl BlobVec {
5050
capacity: usize,
5151
) -> BlobVec {
5252
if item_layout.size() == 0 {
53+
let align = NonZeroUsize::new(item_layout.align()).expect("alignment must be > 0");
5354
BlobVec {
5455
swap_scratch: NonNull::dangling(),
55-
data: NonNull::dangling(),
56+
data: bevy_ptr::dangling_with_align(align),
5657
capacity: usize::MAX,
5758
len: 0,
5859
item_layout,
@@ -405,7 +406,8 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
405406

406407
#[cfg(test)]
407408
mod tests {
408-
use crate::ptr::OwningPtr;
409+
use crate as bevy_ecs; // required for derive macros
410+
use crate::{component::Component, ptr::OwningPtr, world::World};
409411

410412
use super::BlobVec;
411413
use std::{alloc::Layout, cell::RefCell, rc::Rc};
@@ -544,4 +546,28 @@ mod tests {
544546
// SAFETY: drop is able to drop a value of its `item_layout`
545547
let _ = unsafe { BlobVec::new(item_layout, Some(drop), 0) };
546548
}
549+
550+
#[test]
551+
fn aligned_zst() {
552+
// NOTE: This test is explicitly for uncovering potential UB with miri.
553+
554+
#[derive(Component)]
555+
#[repr(align(32))]
556+
struct Zst;
557+
558+
let mut world = World::default();
559+
world.spawn(Zst);
560+
world.spawn(Zst);
561+
world.spawn(Zst);
562+
world.spawn_empty();
563+
564+
let mut count = 0;
565+
566+
let mut q = world.query::<&Zst>();
567+
for &Zst in q.iter(&world) {
568+
count += 1;
569+
}
570+
571+
assert_eq!(count, 3);
572+
}
547573
}

crates/bevy_ptr/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#![no_std]
33
#![warn(missing_docs)]
44

5-
use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
5+
use core::{
6+
cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, num::NonZeroUsize, ptr::NonNull,
7+
};
68

79
/// Type-erased borrow of some unknown type chosen when constructing this type.
810
///
@@ -243,6 +245,14 @@ impl<'a, T> From<&'a [T]> for ThinSlicePtr<'a, T> {
243245
}
244246
}
245247

248+
/// Creates a dangling pointer with specified alignment.
249+
/// See [`NonNull::dangling`].
250+
pub fn dangling_with_align(align: NonZeroUsize) -> NonNull<u8> {
251+
// SAFETY: The pointer will not be null, since it was created
252+
// from the address of a `NonZeroUsize`.
253+
unsafe { NonNull::new_unchecked(align.get() as *mut u8) }
254+
}
255+
246256
mod private {
247257
use core::cell::UnsafeCell;
248258

0 commit comments

Comments
 (0)