Adjust AnyFrameMeta safety conditions

This commit is contained in:
Ruihan Li 2025-03-03 10:30:48 +08:00 committed by Tate, Hongliang Tian
parent 0d36375dfa
commit 0c028a7d8c
3 changed files with 16 additions and 47 deletions

View File

@ -449,16 +449,8 @@ impl<M> Link<M> {
} }
} }
// SAFETY: The size and alignment of `Link<M>` must be within the limits. // SAFETY: If `M::on_drop` reads the page using the provided `VmReader`,
// Also, if `M` is typed, `Link<M>` must not be untyped. // the safety is upheld by the one who implements `AnyFrameMeta` for `M`.
//
// FIXME: We would appreciate constant trait bounds very much, just like:
// ```
// Assert<{ core::mem::size_of::<Link<M>>() < FRAME_METADATA_MAX_SIZE }>: IsTrue,
// Assert<{ core::mem::align_of::<Link<M>>() <= FRAME_METADATA_MAX_ALIGN }>: IsTrue,
// ```
// But due to the broken implementation of `generic_const_exprs` we can't do that.
// It works for `M` that is declared inside OSTD but not for outside types.
unsafe impl<M> AnyFrameMeta for Link<M> unsafe impl<M> AnyFrameMeta for Link<M>
where where
M: AnyFrameMeta, M: AnyFrameMeta,

View File

@ -134,19 +134,18 @@ const_assert_eq!(size_of::<MetaSlot>(), META_SLOT_SIZE);
/// this frame, the `on_drop` method will be called. The `on_drop` /// this frame, the `on_drop` method will be called. The `on_drop`
/// method is called with the physical address of the frame. /// method is called with the physical address of the frame.
/// ///
/// The implemented structure should have a size less than or equal to
/// [`FRAME_METADATA_MAX_SIZE`] and an alignment less than or equal to
/// [`FRAME_METADATA_MAX_ALIGN`]. Otherwise, the metadata type cannot
/// be used because storing it will fail compile-time assertions.
///
/// # Safety /// # Safety
/// ///
/// The implemented structure must have a size less than or equal to /// If `on_drop` reads the page using the provided `VmReader`, the
/// [`FRAME_METADATA_MAX_SIZE`] and an alignment less than or equal to /// implementer must ensure that the frame is safe to read.
/// [`FRAME_METADATA_MAX_ALIGN`].
///
/// The implementer of the `on_drop` method should ensure that the frame is
/// safe to be read.
pub unsafe trait AnyFrameMeta: Any + Send + Sync + Debug + 'static { pub unsafe trait AnyFrameMeta: Any + Send + Sync + Debug + 'static {
/// Called when the last handle to the frame is dropped. /// Called when the last handle to the frame is dropped.
fn on_drop(&mut self, reader: &mut VmReader<Infallible>) { fn on_drop(&mut self, _reader: &mut VmReader<Infallible>) {}
let _ = reader;
}
/// Whether the metadata's associated frame is untyped. /// Whether the metadata's associated frame is untyped.
/// ///
@ -160,18 +159,11 @@ pub unsafe trait AnyFrameMeta: Any + Send + Sync + Debug + 'static {
} }
/// Makes a structure usable as a frame metadata. /// Makes a structure usable as a frame metadata.
///
/// Directly implementing [`AnyFrameMeta`] is not safe since the size and alignment
/// must be checked. This macro provides a safe way to implement the trait with
/// compile-time checks.
#[macro_export] #[macro_export]
macro_rules! impl_frame_meta_for { macro_rules! impl_frame_meta_for {
// Implement without specifying the drop behavior. // Implement without specifying the drop behavior.
($t:ty) => { ($t:ty) => {
use static_assertions::const_assert; // SAFETY: `on_drop` won't read the page.
const_assert!(size_of::<$t>() <= $crate::mm::frame::meta::FRAME_METADATA_MAX_SIZE);
const_assert!(align_of::<$t>() <= $crate::mm::frame::meta::FRAME_METADATA_MAX_ALIGN);
// SAFETY: The size and alignment of the structure are checked.
unsafe impl $crate::mm::frame::meta::AnyFrameMeta for $t {} unsafe impl $crate::mm::frame::meta::AnyFrameMeta for $t {}
}; };
} }
@ -358,11 +350,8 @@ impl MetaSlot {
/// ///
/// The caller should have exclusive access to the metadata slot's fields. /// The caller should have exclusive access to the metadata slot's fields.
pub(super) unsafe fn write_meta<M: AnyFrameMeta>(&self, metadata: M) { pub(super) unsafe fn write_meta<M: AnyFrameMeta>(&self, metadata: M) {
// Checking unsafe preconditions of the `AnyFrameMeta` trait. const { assert!(size_of::<M>() <= FRAME_METADATA_MAX_SIZE) };
// We can't debug assert until we fix the constant generic bonds in const { assert!(align_of::<M>() <= FRAME_METADATA_MAX_ALIGN) };
// the linked list meta.
assert!(size_of::<M>() <= FRAME_METADATA_MAX_SIZE);
assert!(align_of::<M>() <= FRAME_METADATA_MAX_ALIGN);
// SAFETY: Caller ensures that the access to the fields are exclusive. // SAFETY: Caller ensures that the access to the fields are exclusive.
let vtable_ptr = unsafe { &mut *self.vtable_ptr.get() }; let vtable_ptr = unsafe { &mut *self.vtable_ptr.get() };
@ -372,7 +361,7 @@ impl MetaSlot {
// SAFETY: // SAFETY:
// 1. `ptr` points to the metadata storage. // 1. `ptr` points to the metadata storage.
// 2. The size and the alignment of the metadata storage is large enough to hold `M` // 2. The size and the alignment of the metadata storage is large enough to hold `M`
// (guaranteed by the safety requirement of the `AnyFrameMeta` trait). // (guaranteed by the const assertions above).
// 3. We have exclusive access to the metadata storage (guaranteed by the caller). // 3. We have exclusive access to the metadata storage (guaranteed by the caller).
unsafe { ptr.cast::<M>().write(metadata) }; unsafe { ptr.cast::<M>().write(metadata) };
} }

View File

@ -34,19 +34,12 @@ pub type UFrame = Frame<dyn AnyUFrameMeta>;
/// Makes a structure usable as untyped frame metadata. /// Makes a structure usable as untyped frame metadata.
/// ///
/// Directly implementing [`AnyFrameMeta`] is not safe since the size and
/// alignment must be checked. This macro provides a safe way to implement both
/// [`AnyFrameMeta`] and [`AnyUFrameMeta`] with compile-time checks.
///
/// If this macro is used for built-in typed frame metadata, it won't compile. /// If this macro is used for built-in typed frame metadata, it won't compile.
#[macro_export] #[macro_export]
macro_rules! impl_untyped_frame_meta_for { macro_rules! impl_untyped_frame_meta_for {
// Implement without specifying the drop behavior. // Implement without specifying the drop behavior.
($t:ty) => { ($t:ty) => {
use static_assertions::const_assert; // SAFETY: Untyped frames can be safely read.
const_assert!(size_of::<$t>() <= $crate::mm::frame::meta::FRAME_METADATA_MAX_SIZE);
const_assert!(align_of::<$t>() <= $crate::mm::frame::meta::FRAME_METADATA_MAX_ALIGN);
// SAFETY: The size and alignment of the structure are checked.
unsafe impl $crate::mm::frame::meta::AnyFrameMeta for $t { unsafe impl $crate::mm::frame::meta::AnyFrameMeta for $t {
fn is_untyped(&self) -> bool { fn is_untyped(&self) -> bool {
true true
@ -56,12 +49,7 @@ macro_rules! impl_untyped_frame_meta_for {
}; };
// Implement with a customized drop function. // Implement with a customized drop function.
($t:ty, $body:expr) => { ($t:ty, $body:expr) => {
use static_assertions::const_assert; // SAFETY: Untyped frames can be safely read.
const_assert!(size_of::<$t>() <= $crate::mm::frame::meta::FRAME_METADATA_MAX_SIZE);
const_assert!(align_of::<$t>() <= $crate::mm::frame::meta::FRAME_METADATA_MAX_ALIGN);
// SAFETY: The size and alignment of the structure are checked.
// Outside OSTD the user cannot implement a `on_drop` method for typed
// frames. And untyped frames can be safely read.
unsafe impl $crate::mm::frame::meta::AnyFrameMeta for $t { unsafe impl $crate::mm::frame::meta::AnyFrameMeta for $t {
fn on_drop(&mut self, reader: &mut $crate::mm::VmReader<$crate::mm::Infallible>) { fn on_drop(&mut self, reader: &mut $crate::mm::VmReader<$crate::mm::Infallible>) {
$body $body