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.
This commit is contained in:
parent
9498bfffcb
commit
3ac06b57e9
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<u8> {
|
||||
// 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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user