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,
|
capacity: usize,
|
||||||
) -> BlobVec {
|
) -> BlobVec {
|
||||||
if item_layout.size() == 0 {
|
if item_layout.size() == 0 {
|
||||||
|
let align = NonZeroUsize::new(item_layout.align()).expect("alignment must be > 0");
|
||||||
BlobVec {
|
BlobVec {
|
||||||
swap_scratch: NonNull::dangling(),
|
swap_scratch: NonNull::dangling(),
|
||||||
data: NonNull::dangling(),
|
data: bevy_ptr::dangling_with_align(align),
|
||||||
capacity: usize::MAX,
|
capacity: usize::MAX,
|
||||||
len: 0,
|
len: 0,
|
||||||
item_layout,
|
item_layout,
|
||||||
@ -405,7 +406,8 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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 super::BlobVec;
|
||||||
use std::{alloc::Layout, cell::RefCell, rc::Rc};
|
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`
|
// SAFETY: drop is able to drop a value of its `item_layout`
|
||||||
let _ = unsafe { BlobVec::new(item_layout, Some(drop), 0) };
|
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]
|
#![no_std]
|
||||||
#![warn(missing_docs)]
|
#![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.
|
/// 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 {
|
mod private {
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::UnsafeCell;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user