From af561db3e1bbceb6e84ff1308cf094e9cb82deec Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 22 Jun 2026 10:59:00 +0200 Subject: [PATCH 01/10] multiboot2*: make total size authoritative Require Header implementors to report the full structure size and derive payload_len() from it. Validate undersized reports before building DynSizedStructure values, and use the reported total size for tag iteration as well. This makes the reported size the single source of truth. The old shape spread the invariant across payload_len(), total_size(), and per-type assertions, which was easy to drift out of sync. With one authoritative value, the parsing code can validate once and reuse the same size for construction, iteration, and downstream casts. Update the concrete header types in multiboot2 and multiboot2-header to the new contract, and add a regression test for a reported size that is smaller than the fixed header. Breaking: Header implementors now provide total_size() instead of payload_len(). multiboot2*: simplify MaybeDynSize trait for implementors --- multiboot2-common/CHANGELOG.md | 2 ++ multiboot2-common/src/iter.rs | 2 +- multiboot2-common/src/lib.rs | 53 +++++++++++++++++++++++------ multiboot2-common/src/tag.rs | 2 +- multiboot2-common/src/test_utils.rs | 4 +-- multiboot2-header/src/header.rs | 5 ++- multiboot2-header/src/tags.rs | 4 +-- multiboot2/src/boot_information.rs | 5 ++- multiboot2/src/tag.rs | 5 ++- 9 files changed, 56 insertions(+), 26 deletions(-) diff --git a/multiboot2-common/CHANGELOG.md b/multiboot2-common/CHANGELOG.md index 1997f1cb..2de83395 100644 --- a/multiboot2-common/CHANGELOG.md +++ b/multiboot2-common/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- **Breaking:** `Header` now requires `total_size()` and derives + `payload_len()` from it. - Added validation for complete padded tag sequences. - Added size details to memory validation errors. - Fixed validation for dynamically sized structures whose reported total size diff --git a/multiboot2-common/src/iter.rs b/multiboot2-common/src/iter.rs index 7b029c59..1adec8a9 100644 --- a/multiboot2-common/src/iter.rs +++ b/multiboot2-common/src/iter.rs @@ -63,7 +63,7 @@ impl<'a, H: Header + 'a> Iterator for TagIter<'a, H> { // See . let slice = { let from = self.next_tag_offset; - let len = size_of::() + tag_hdr.payload_len(); + let len = tag_hdr.total_size(); let to = from + len; // The size of (the allocation for) a value is always a multiple of diff --git a/multiboot2-common/src/lib.rs b/multiboot2-common/src/lib.rs index af0fe646..3dd0d884 100644 --- a/multiboot2-common/src/lib.rs +++ b/multiboot2-common/src/lib.rs @@ -274,16 +274,18 @@ pub const ALIGNMENT: usize = 8; /// The alignment of implementors **must** be the compatible with the demands /// for the corresponding structure, which typically is [`ALIGNMENT`]. pub trait Header: Clone + Sized + PartialEq + Eq + Debug { - /// Returns the length of the payload, i.e., the bytes that are additional - /// to the header. The value is measured in bytes. + /// Returns the total size of the structure in bytes, including the fixed + /// header and any dynamic payload. #[must_use] - fn payload_len(&self) -> usize; + fn total_size(&self) -> usize; - /// Returns the total size of the struct, thus the size of the header itself - /// plus [`Header::payload_len`]. + /// Returns the length of the payload, i.e., the bytes that are additional + /// to the header. The value is measured in bytes. #[must_use] - fn total_size(&self) -> usize { - size_of::() + self.payload_len() + fn payload_len(&self) -> usize { + let total_size = self.total_size(); + assert!(total_size >= size_of::()); + total_size - size_of::() } /// Updates the header with the given `total_size`. @@ -294,7 +296,7 @@ pub trait Header: Clone + Sized + PartialEq + Eq + Debug { /// and a dynamic amount of bytes without hidden implicit padding. /// /// This structures combines a [`Header`] with the logically owned data by -/// that header according to the reported [`Header::payload_len`]. Instances +/// that header according to the reported [`Header::total_size`]. Instances /// guarantees that the memory requirements promised in the crates description /// are respected. /// @@ -333,14 +335,18 @@ impl DynSizedStructure { let ptr = bytes.as_ptr().cast::(); let hdr = unsafe { &*ptr }; - let payload_len = hdr.payload_len(); - let total_size = size_of::() + payload_len; + let total_size = hdr.total_size(); + let header_size = size_of::(); + if total_size < header_size { + return Err(MemoryError::SizeInsufficient(total_size, header_size)); + } if total_size > bytes.len() { return Err(MemoryError::InvalidReportedTotalSize( total_size, bytes.len(), )); } + let payload_len = total_size - header_size; // At this point we know that the memory slice fulfills the base // assumptions and requirements. Now, we safety can create the fat @@ -369,8 +375,13 @@ impl DynSizedStructure { pub unsafe fn ref_from_ptr<'a>(ptr: NonNull) -> Result<&'a Self, MemoryError> { let ptr = ptr.as_ptr().cast_const(); let hdr = unsafe { &*ptr }; + let total_size = hdr.total_size(); + let header_size = size_of::(); + if total_size < header_size { + return Err(MemoryError::SizeInsufficient(total_size, header_size)); + } - let slice = unsafe { slice::from_raw_parts(ptr.cast::(), hdr.total_size()) }; + let slice = unsafe { slice::from_raw_parts(ptr.cast::(), total_size) }; Self::ref_from_slice(slice) } @@ -617,4 +628,24 @@ mod tests { Err(MemoryError::InvalidReportedTotalSize(24, 16)) ); } + + #[test] + fn test_ref_from_slice_rejects_too_small_reported_size() { + #[rustfmt::skip] + let bytes = AlignedBytes::new( + [ + 0x37, 0x13, 0, 0, + /* Tag size */ + 4, 0, 0, 0, + /* Remaining bytes are irrelevant. */ + 0, 1, 2, 3, + 0, 0, 0, 0, + ], + ); + + assert_eq!( + DynSizedStructure::::ref_from_slice(bytes.borrow()), + Err(MemoryError::SizeInsufficient(4, 8)) + ); + } } diff --git a/multiboot2-common/src/tag.rs b/multiboot2-common/src/tag.rs index 6e3947da..ae4464dd 100644 --- a/multiboot2-common/src/tag.rs +++ b/multiboot2-common/src/tag.rs @@ -97,6 +97,6 @@ impl MaybeDynSized for DynSizedStructure { const BASE_SIZE: usize = size_of::(); fn dst_len(header: &Self::Header) -> Self::Metadata { - header.payload_len() + header.total_size() - size_of::() } } diff --git a/multiboot2-common/src/test_utils.rs b/multiboot2-common/src/test_utils.rs index 4f0f74ca..702568a3 100644 --- a/multiboot2-common/src/test_utils.rs +++ b/multiboot2-common/src/test_utils.rs @@ -69,8 +69,8 @@ impl DummyTestHeader { } impl Header for DummyTestHeader { - fn payload_len(&self) -> usize { - self.size as usize - size_of::() + fn total_size(&self) -> usize { + self.size as usize } fn set_size(&mut self, total_size: usize) { diff --git a/multiboot2-header/src/header.rs b/multiboot2-header/src/header.rs index aca26837..f3d7ff3f 100644 --- a/multiboot2-header/src/header.rs +++ b/multiboot2-header/src/header.rs @@ -393,9 +393,8 @@ impl Multiboot2BasicHeader { } impl Header for Multiboot2BasicHeader { - fn payload_len(&self) -> usize { - assert!(self.length as usize >= size_of::()); - self.length as usize - size_of::() + fn total_size(&self) -> usize { + self.length as usize } fn set_size(&mut self, total_size: usize) { diff --git a/multiboot2-header/src/tags.rs b/multiboot2-header/src/tags.rs index c56c3a6a..06a10acf 100644 --- a/multiboot2-header/src/tags.rs +++ b/multiboot2-header/src/tags.rs @@ -106,8 +106,8 @@ impl HeaderTagHeader { } impl Header for HeaderTagHeader { - fn payload_len(&self) -> usize { - self.size as usize - size_of::() + fn total_size(&self) -> usize { + self.size as usize } fn set_size(&mut self, total_size: usize) { diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 18074334..82e959e6 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -56,9 +56,8 @@ impl BootInformationHeader { } impl Header for BootInformationHeader { - fn payload_len(&self) -> usize { - assert!(self.total_size as usize >= size_of::()); - self.total_size as usize - size_of::() + fn total_size(&self) -> usize { + self.total_size as usize } fn set_size(&mut self, total_size: usize) { diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index 239b8564..f53991c1 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -33,9 +33,8 @@ impl TagHeader { } impl Header for TagHeader { - fn payload_len(&self) -> usize { - assert!(self.size as usize >= size_of::()); - self.size as usize - size_of::() + fn total_size(&self) -> usize { + self.size as usize } fn set_size(&mut self, total_size: usize) { From 0cd2fd90d081d34e4c78d0b2965cbf74bc94eb2d Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 23 Jun 2026 09:21:59 +0200 Subject: [PATCH 02/10] multiboot2*: simplify MaybeDynSize trait for implementors --- multiboot2-common/src/lib.rs | 5 ++++- multiboot2-common/src/tag.rs | 14 ++++++++++++-- multiboot2-header/src/address.rs | 2 -- multiboot2-header/src/console.rs | 2 -- multiboot2-header/src/end.rs | 2 -- multiboot2-header/src/entry_efi_32.rs | 2 -- multiboot2-header/src/entry_efi_64.rs | 2 -- multiboot2-header/src/framebuffer.rs | 2 -- multiboot2-header/src/header.rs | 5 ++++- multiboot2-header/src/module_align.rs | 2 -- multiboot2-header/src/relocatable.rs | 2 -- multiboot2-header/src/uefi_bs.rs | 2 -- multiboot2/src/apm.rs | 2 -- multiboot2/src/boot_information.rs | 5 ++++- multiboot2/src/bootdev.rs | 2 -- multiboot2/src/efi.rs | 10 ---------- multiboot2/src/end.rs | 2 -- multiboot2/src/image_load_addr.rs | 2 -- multiboot2/src/lib.rs | 2 -- multiboot2/src/memory_map.rs | 2 -- multiboot2/src/rsdp.rs | 4 ---- multiboot2/src/vbe_info.rs | 2 -- 22 files changed, 24 insertions(+), 51 deletions(-) diff --git a/multiboot2-common/src/lib.rs b/multiboot2-common/src/lib.rs index 3dd0d884..00c58217 100644 --- a/multiboot2-common/src/lib.rs +++ b/multiboot2-common/src/lib.rs @@ -416,7 +416,10 @@ impl DynSizedStructure { /// # Panics /// This panics if there is a size mismatch. However, this should never be /// the case if all types follow their documented requirements. - pub fn cast + ?Sized>(&self) -> &T { + pub fn cast + ?Sized>(&self) -> &T + where + T::Metadata: Default, + { // Thin or fat pointer, depending on type. // However, only thin ptr is needed. let base_ptr = ptr::addr_of!(*self); diff --git a/multiboot2-common/src/tag.rs b/multiboot2-common/src/tag.rs index ae4464dd..c6405f1a 100644 --- a/multiboot2-common/src/tag.rs +++ b/multiboot2-common/src/tag.rs @@ -9,7 +9,10 @@ use ptr_meta::Pointee; /// [`DynSizedStructure::cast`]. /// /// Structs that are a DST must provide a **correct** [`MaybeDynSized::dst_len`] -/// implementation. +/// implementation. The needed metadata type is either `()` for sized types or +/// `usize` for dynamically sized types. For sized types, there is a default +/// implementation. Only dynamically sized types need to implement +/// [`MaybeDynSized::dst_len`]. /// /// # ABI /// Implementors **must** use `#[repr(C)]`. As there might be padding necessary @@ -43,7 +46,14 @@ pub trait MaybeDynSized: Pointee { /// /// For sized tags, this just returns `()`. For DSTs, this returns an /// `usize`. - fn dst_len(header: &Self::Header) -> Self::Metadata; + fn dst_len(header: &Self::Header) -> Self::Metadata + where + // Either `()` or `usize`, never something else + Self::Metadata: Default, + { + let _ = header; + Default::default() + } /// Returns the corresponding [`Header`]. fn header(&self) -> &Self::Header { diff --git a/multiboot2-header/src/address.rs b/multiboot2-header/src/address.rs index 7cd33c43..5b9b422b 100644 --- a/multiboot2-header/src/address.rs +++ b/multiboot2-header/src/address.rs @@ -90,8 +90,6 @@ impl MaybeDynSized for AddressHeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for AddressHeaderTag { diff --git a/multiboot2-header/src/console.rs b/multiboot2-header/src/console.rs index df65d846..3f649420 100644 --- a/multiboot2-header/src/console.rs +++ b/multiboot2-header/src/console.rs @@ -61,8 +61,6 @@ impl MaybeDynSized for ConsoleHeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::() + size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for ConsoleHeaderTag { diff --git a/multiboot2-header/src/end.rs b/multiboot2-header/src/end.rs index 49620d00..cf25753c 100644 --- a/multiboot2-header/src/end.rs +++ b/multiboot2-header/src/end.rs @@ -49,8 +49,6 @@ impl MaybeDynSized for EndHeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for EndHeaderTag { diff --git a/multiboot2-header/src/entry_efi_32.rs b/multiboot2-header/src/entry_efi_32.rs index 66ddb8d1..e808179a 100644 --- a/multiboot2-header/src/entry_efi_32.rs +++ b/multiboot2-header/src/entry_efi_32.rs @@ -70,8 +70,6 @@ impl MaybeDynSized for EntryEfi32HeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::() + size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for EntryEfi32HeaderTag { diff --git a/multiboot2-header/src/entry_efi_64.rs b/multiboot2-header/src/entry_efi_64.rs index 9c70b555..856a5d48 100644 --- a/multiboot2-header/src/entry_efi_64.rs +++ b/multiboot2-header/src/entry_efi_64.rs @@ -70,8 +70,6 @@ impl MaybeDynSized for EntryEfi64HeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::() + size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for EntryEfi64HeaderTag { diff --git a/multiboot2-header/src/framebuffer.rs b/multiboot2-header/src/framebuffer.rs index 6a1af645..b9091947 100644 --- a/multiboot2-header/src/framebuffer.rs +++ b/multiboot2-header/src/framebuffer.rs @@ -69,8 +69,6 @@ impl MaybeDynSized for FramebufferHeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::() + 3 * size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for FramebufferHeaderTag { diff --git a/multiboot2-header/src/header.rs b/multiboot2-header/src/header.rs index f3d7ff3f..97f96000 100644 --- a/multiboot2-header/src/header.rs +++ b/multiboot2-header/src/header.rs @@ -276,7 +276,10 @@ impl<'a> Multiboot2Header<'a> { #[must_use] fn get_tag + ?Sized + 'a>( &'a self, - ) -> Option<&'a T> { + ) -> Option<&'a T> + where + T::Metadata: Default, + { self.iter() .find(|tag| tag.header().typ() == T::ID) .map(|tag| tag.cast::()) diff --git a/multiboot2-header/src/module_align.rs b/multiboot2-header/src/module_align.rs index 8714c8d9..2055d7b7 100644 --- a/multiboot2-header/src/module_align.rs +++ b/multiboot2-header/src/module_align.rs @@ -40,8 +40,6 @@ impl MaybeDynSized for ModuleAlignHeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for ModuleAlignHeaderTag { diff --git a/multiboot2-header/src/relocatable.rs b/multiboot2-header/src/relocatable.rs index 3bac9119..3eaa059d 100644 --- a/multiboot2-header/src/relocatable.rs +++ b/multiboot2-header/src/relocatable.rs @@ -116,8 +116,6 @@ impl MaybeDynSized for RelocatableHeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for RelocatableHeaderTag { diff --git a/multiboot2-header/src/uefi_bs.rs b/multiboot2-header/src/uefi_bs.rs index c69b9f10..241f62a9 100644 --- a/multiboot2-header/src/uefi_bs.rs +++ b/multiboot2-header/src/uefi_bs.rs @@ -40,8 +40,6 @@ impl MaybeDynSized for EfiBootServiceHeaderTag { type Header = HeaderTagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_header: &Self::Header) -> Self::Metadata {} } impl Tag for EfiBootServiceHeaderTag { diff --git a/multiboot2/src/apm.rs b/multiboot2/src/apm.rs index bcc73dcc..70a438b7 100644 --- a/multiboot2/src/apm.rs +++ b/multiboot2/src/apm.rs @@ -114,8 +114,6 @@ impl MaybeDynSized for ApmTag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for ApmTag { diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 82e959e6..5537bd5a 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -401,7 +401,10 @@ impl<'a> BootInformation<'a> { #[must_use] pub fn get_tag + ?Sized + 'a>( &'a self, - ) -> Option<&'a T> { + ) -> Option<&'a T> + where + T::Metadata: Default, + { self.tags() .find(|tag| tag.header().typ == T::ID) .map(|tag| tag.cast::()) diff --git a/multiboot2/src/bootdev.rs b/multiboot2/src/bootdev.rs index 75a705a6..9e2cf5e2 100644 --- a/multiboot2/src/bootdev.rs +++ b/multiboot2/src/bootdev.rs @@ -55,8 +55,6 @@ impl MaybeDynSized for BootdevTag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for BootdevTag { diff --git a/multiboot2/src/efi.rs b/multiboot2/src/efi.rs index 5320534d..287c8cef 100644 --- a/multiboot2/src/efi.rs +++ b/multiboot2/src/efi.rs @@ -41,8 +41,6 @@ impl MaybeDynSized for EFISdt32Tag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for EFISdt32Tag { @@ -80,8 +78,6 @@ impl MaybeDynSized for EFISdt64Tag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for EFISdt64Tag { @@ -122,8 +118,6 @@ impl MaybeDynSized for EFIImageHandle32Tag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for EFIImageHandle32Tag { @@ -162,8 +156,6 @@ impl MaybeDynSized for EFIImageHandle64Tag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for EFIImageHandle64Tag { @@ -200,8 +192,6 @@ impl MaybeDynSized for EFIBootServicesNotExitedTag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for EFIBootServicesNotExitedTag { diff --git a/multiboot2/src/end.rs b/multiboot2/src/end.rs index e4bdd5cb..c7099bc1 100644 --- a/multiboot2/src/end.rs +++ b/multiboot2/src/end.rs @@ -22,8 +22,6 @@ impl MaybeDynSized for EndTag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for EndTag { diff --git a/multiboot2/src/image_load_addr.rs b/multiboot2/src/image_load_addr.rs index 5f464428..67fda150 100644 --- a/multiboot2/src/image_load_addr.rs +++ b/multiboot2/src/image_load_addr.rs @@ -36,8 +36,6 @@ impl MaybeDynSized for ImageLoadPhysAddrTag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for ImageLoadPhysAddrTag { diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 0004114b..970686b0 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -1204,8 +1204,6 @@ mod tests { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) -> Self::Metadata {} } impl Tag for CustomTag { diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index 98233078..eda47e26 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -293,8 +293,6 @@ impl MaybeDynSized for BasicMemoryInfoTag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for BasicMemoryInfoTag { diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index 2542bc51..e47d8d86 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -92,8 +92,6 @@ impl MaybeDynSized for RsdpV1Tag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for RsdpV1Tag { @@ -200,8 +198,6 @@ impl MaybeDynSized for RsdpV2Tag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for RsdpV2Tag { diff --git a/multiboot2/src/vbe_info.rs b/multiboot2/src/vbe_info.rs index 1677382c..2d22d0ba 100644 --- a/multiboot2/src/vbe_info.rs +++ b/multiboot2/src/vbe_info.rs @@ -86,8 +86,6 @@ impl MaybeDynSized for VBEInfoTag { type Header = TagHeader; const BASE_SIZE: usize = size_of::(); - - fn dst_len(_: &TagHeader) {} } impl Tag for VBEInfoTag { From f69449f592ff611f2b8f267641bf15c935efdec4 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 22 Jun 2026 11:16:36 +0200 Subject: [PATCH 03/10] treewide: multiple smaller safety fixes --- multiboot2-common/src/iter.rs | 11 +++++++++-- multiboot2-common/src/lib.rs | 2 +- multiboot2-common/src/tag.rs | 2 +- multiboot2-header/src/end.rs | 2 +- multiboot2-header/src/header.rs | 9 +++++---- multiboot2-header/src/tags.rs | 4 ++-- multiboot2/src/boot_information.rs | 7 ++++--- multiboot2/src/tag.rs | 2 +- 8 files changed, 24 insertions(+), 15 deletions(-) diff --git a/multiboot2-common/src/iter.rs b/multiboot2-common/src/iter.rs index 1adec8a9..fd37a894 100644 --- a/multiboot2-common/src/iter.rs +++ b/multiboot2-common/src/iter.rs @@ -31,10 +31,15 @@ pub struct TagIter<'a, H: Header> { impl<'a, H: Header> TagIter<'a, H> { /// Creates a new iterator. + /// + /// # Safety + /// + /// Callers must ensure that the whole chain of tags (with their reported + /// sizes) is valid and fits within the memory slice. #[must_use] // TODO we could take a BytesRef here, but the surrounding code should be // bullet-proof enough. - pub fn new(mem: &'a [u8]) -> Self { + pub unsafe fn new(mem: &'a [u8]) -> Self { // Assert alignment. assert_eq!(mem.as_ptr().align_offset(ALIGNMENT), 0); @@ -78,6 +83,7 @@ impl<'a, H: Header + 'a> Iterator for TagIter<'a, H> { }; // unwrap: We should not fail at this point. + // In any ::load() before, we already validated the whole chain of tags. let tag = DynSizedStructure::ref_from_slice(slice).unwrap(); Some(tag) } @@ -108,7 +114,8 @@ mod tests { 8, 0, 0, 0, ], ); - let mut iter = TagIter::::new(bytes.borrow()); + // SAFETY: Chain of tags is valid. + let mut iter = unsafe { TagIter::::new(bytes.borrow()) }; let first = iter.next().unwrap(); assert_eq!(first.header().typ(), 0xff); assert_eq!(first.header().size(), 8); diff --git a/multiboot2-common/src/lib.rs b/multiboot2-common/src/lib.rs index 00c58217..9dd6c566 100644 --- a/multiboot2-common/src/lib.rs +++ b/multiboot2-common/src/lib.rs @@ -470,7 +470,7 @@ pub fn validate_tag_sequence( let tag = &bytes[offset..]; let total_size = - u32::from_ne_bytes(tag[4..8].try_into().expect("slice has exactly 4 bytes")) as usize; + u32::from_le_bytes(tag[4..8].try_into().expect("slice has exactly 4 bytes")) as usize; if total_size < TAG_HEADER_SIZE { return Err(MemoryError::SizeInsufficient(total_size, TAG_HEADER_SIZE)); diff --git a/multiboot2-common/src/tag.rs b/multiboot2-common/src/tag.rs index c6405f1a..c655750b 100644 --- a/multiboot2-common/src/tag.rs +++ b/multiboot2-common/src/tag.rs @@ -107,6 +107,6 @@ impl MaybeDynSized for DynSizedStructure { const BASE_SIZE: usize = size_of::(); fn dst_len(header: &Self::Header) -> Self::Metadata { - header.total_size() - size_of::() + header.payload_len() } } diff --git a/multiboot2-header/src/end.rs b/multiboot2-header/src/end.rs index cf25753c..42c1f7e5 100644 --- a/multiboot2-header/src/end.rs +++ b/multiboot2-header/src/end.rs @@ -3,7 +3,7 @@ use multiboot2_common::{MaybeDynSized, Tag}; /// Terminates a list of optional tags in a Multiboot2 header. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct EndHeaderTag { header: HeaderTagHeader, } diff --git a/multiboot2-header/src/header.rs b/multiboot2-header/src/header.rs index 97f96000..15055e40 100644 --- a/multiboot2-header/src/header.rs +++ b/multiboot2-header/src/header.rs @@ -69,9 +69,9 @@ impl<'a> Multiboot2Header<'a> { /// Checks whether the header has a valid, complete tag sequence. fn has_valid_tag_sequence(&self) -> Result { validate_tag_sequence(self.0.payload(), |tag| { - let typ = u16::from_ne_bytes(tag[0..2].try_into().unwrap()); - let flags = u16::from_ne_bytes(tag[2..4].try_into().unwrap()); - let size = u32::from_ne_bytes(tag[4..8].try_into().unwrap()) as usize; + let typ = u16::from_le_bytes(tag[0..2].try_into().unwrap()); + let flags = u16::from_le_bytes(tag[2..4].try_into().unwrap()); + let size = u32::from_le_bytes(tag[4..8].try_into().unwrap()) as usize; typ == HeaderTagType::End as u16 && flags == crate::HeaderTagFlag::Required as u16 @@ -170,7 +170,8 @@ impl<'a> Multiboot2Header<'a> { /// Returns a [`TagIter`]. #[must_use] pub fn iter(&self) -> TagIter<'_> { - TagIter::new(self.0.payload()) + // SAFETY: We validated the chain of tags beforehand. + unsafe { TagIter::new(self.0.payload()) } } /// Wrapper around [`Multiboot2BasicHeader::verify_checksum`]. diff --git a/multiboot2-header/src/tags.rs b/multiboot2-header/src/tags.rs index 06a10acf..77cf2a32 100644 --- a/multiboot2-header/src/tags.rs +++ b/multiboot2-header/src/tags.rs @@ -71,12 +71,12 @@ pub enum HeaderTagFlag { /// The common header that all header tags share. Specific tags may have /// additional fields that depend on the `typ` and the `size` field. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct HeaderTagHeader { typ: HeaderTagType, /* u16 */ flags: HeaderTagFlag, /* u16 */ size: u32, - // Followed by optional additional tag specific fields. + // Followed by optional additional tag-specific fields. } impl HeaderTagHeader { diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 5537bd5a..f9097523 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -115,8 +115,8 @@ impl<'a> BootInformation<'a> { /// Checks if the MBI has a valid, complete tag sequence. fn has_valid_tag_sequence(&self) -> Result { validate_tag_sequence(self.0.payload(), |tag| { - let typ = u32::from_ne_bytes(tag[0..4].try_into().unwrap()); - let size = u32::from_ne_bytes(tag[4..8].try_into().unwrap()) as usize; + let typ = u32::from_le_bytes(tag[0..4].try_into().unwrap()); + let size = u32::from_le_bytes(tag[4..8].try_into().unwrap()) as usize; typ == TagType::End.val() && size == size_of::() }) @@ -417,7 +417,8 @@ impl<'a> BootInformation<'a> { /// tag getters as normal bootloaders provide most tags only once. #[must_use] pub fn tags(&self) -> TagIter<'_> { - TagIter::new(self.0.payload()) + // SAFETY: We validated the chain of tags beforehand. + unsafe { TagIter::new(self.0.payload()) } } } diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index f53991c1..574be7b2 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -11,7 +11,7 @@ use multiboot2_common::Header; /// /// It is the sized counterpart of `GenericTag`, an internal type. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[repr(C, align(8))] // Alignment also propagates to all tag types using this. +#[repr(C, align(8))] pub struct TagHeader { /// The ABI-compatible [`TagType`]. /// From 4a9bc13a0f292c84e6f85c41521db79e3c211aaf Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 22 Jun 2026 12:51:21 +0200 Subject: [PATCH 04/10] treewide: enforce safety comments Add precise SAFETY comments end enable clippy enforcement for safety comments. --- multiboot2-common/src/boxed.rs | 12 +++++-- multiboot2-common/src/iter.rs | 9 +++++- multiboot2-common/src/lib.rs | 19 ++++++++--- multiboot2-common/src/tag.rs | 4 +++ multiboot2-header/src/builder.rs | 2 ++ multiboot2-header/src/header.rs | 13 +++++++- multiboot2-header/src/information_request.rs | 1 + multiboot2-header/src/lib.rs | 1 + multiboot2/src/boot_information.rs | 2 ++ multiboot2/src/builder.rs | 2 ++ multiboot2/src/end.rs | 2 ++ multiboot2/src/framebuffer.rs | 4 +-- multiboot2/src/lib.rs | 34 ++++++++++++++++++++ multiboot2/src/memory_map.rs | 22 +++++++++---- multiboot2/src/rsdp.rs | 2 ++ 15 files changed, 112 insertions(+), 17 deletions(-) diff --git a/multiboot2-common/src/boxed.rs b/multiboot2-common/src/boxed.rs index 9f3b0595..4f4a5323 100644 --- a/multiboot2-common/src/boxed.rs +++ b/multiboot2-common/src/boxed.rs @@ -37,6 +37,7 @@ pub fn new_boxed + ?Sized>( // See let alloc_size = increase_to_alignment(tag_size); let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap(); + // SAFETY: `layout` matches the requested allocation size and alignment. let heap_ptr = unsafe { alloc::alloc::alloc(layout) }; assert!(!heap_ptr.is_null()); @@ -44,6 +45,8 @@ pub fn new_boxed + ?Sized>( { let len = size_of::(); let ptr = core::ptr::addr_of!(header); + // SAFETY: `header` is a fully initialized stack value and `heap_ptr` + // points into the freshly allocated destination buffer. unsafe { ptr::copy_nonoverlapping(ptr.cast::(), heap_ptr, len); } @@ -55,16 +58,21 @@ pub fn new_boxed + ?Sized>( for &bytes in additional_bytes_slices { let len = bytes.len(); let src = bytes.as_ptr(); + let dst = heap_ptr.wrapping_add(write_offset); + // SAFETY: `src` is a valid slice and `dst` stays inside the + // allocated object without overlapping `src`. unsafe { - let dst = heap_ptr.add(write_offset); ptr::copy_nonoverlapping(src, dst, len); - write_offset += len; } + write_offset += len; } } // This is a fat pointer for DSTs and a thin pointer for sized `T`s. + // SAFETY: The allocation was sized for `T` and all bytes up to the + // reported dynamic length were initialized above. let ptr: *mut T = ptr_meta::from_raw_parts_mut(heap_ptr.cast(), T::dst_len(&header)); + // SAFETY: `ptr` points to the initialized allocation described above. let reference = unsafe { Box::from_raw(ptr) }; // If this panic triggers, there is a fundamental flaw in my logic. This is diff --git a/multiboot2-common/src/iter.rs b/multiboot2-common/src/iter.rs index fd37a894..4eb3a863 100644 --- a/multiboot2-common/src/iter.rs +++ b/multiboot2-common/src/iter.rs @@ -60,7 +60,14 @@ impl<'a, H: Header + 'a> Iterator for TagIter<'a, H> { } assert!(self.next_tag_offset < self.buffer.len()); - let ptr = unsafe { self.buffer.as_ptr().add(self.next_tag_offset) }.cast::(); + let ptr = self + .buffer + .as_ptr() + .wrapping_add(self.next_tag_offset) + .cast::(); + // SAFETY: `new()` requires a validated, aligned tag chain and the + // current offset is checked to stay within the buffer before this + // dereference. let tag_hdr = unsafe { &*ptr }; // Get relevant byte portion for the next tag. This includes padding diff --git a/multiboot2-common/src/lib.rs b/multiboot2-common/src/lib.rs index 9dd6c566..669f2c64 100644 --- a/multiboot2-common/src/lib.rs +++ b/multiboot2-common/src/lib.rs @@ -63,7 +63,7 @@ //! indicates the total size of the structure. This is roughly translated to the //! following rusty base type: //! -//! ```ignore +//! ```rust,ignore //! #[repr(C, align(8))] //! struct DynStructure { //! header: MyHeader, @@ -91,7 +91,7 @@ //! //! Note that we also have structures (tags) in Multiboot2 that looks like this: //! -//! ```ignore +//! ```rust,ignore //! #[repr(C, align(8))] //! struct DynStructure { //! header: MyHeader, @@ -102,7 +102,7 @@ //! //! or //! -//! ```ignore +//! ```rust,ignore //! #[repr(C, align(8))] //! struct CommandLineTag { //! header: TagHeader, @@ -224,8 +224,9 @@ #![deny( clippy::all, clippy::cargo, - clippy::must_use_candidate, clippy::nursery, + clippy::must_use_candidate, + clippy::undocumented_unsafe_blocks, missing_debug_implementations, missing_docs, rustdoc::all @@ -333,6 +334,8 @@ impl DynSizedStructure { /// from the given [`BytesRef`]. pub fn ref_from_bytes(bytes: BytesRef<'_, H>) -> Result<&Self, MemoryError> { let ptr = bytes.as_ptr().cast::(); + // SAFETY: `BytesRef` guarantees alignment and that the buffer covers + // at least the fixed header size. let hdr = unsafe { &*ptr }; let total_size = hdr.total_size(); @@ -355,6 +358,8 @@ impl DynSizedStructure { let dst_size = payload_len; // Create fat pointer for the DST. let ptr = ptr_meta::from_raw_parts(ptr.cast(), dst_size); + // SAFETY: The allocation was sized from the validated reported total + // size, so the fat pointer refers to initialized memory. let reference = unsafe { &*ptr }; Ok(reference) } @@ -374,6 +379,8 @@ impl DynSizedStructure { /// The caller must ensure that the function operates on valid memory. pub unsafe fn ref_from_ptr<'a>(ptr: NonNull) -> Result<&'a Self, MemoryError> { let ptr = ptr.as_ptr().cast_const(); + // SAFETY: `ptr` came from a valid pointer to the header; we only read + // the reported total size and immediately re-slice that range. let hdr = unsafe { &*ptr }; let total_size = hdr.total_size(); let header_size = size_of::(); @@ -381,6 +388,8 @@ impl DynSizedStructure { return Err(MemoryError::SizeInsufficient(total_size, header_size)); } + // SAFETY: `total_size` came from the validated header and matches the + // readable byte range for the structure. let slice = unsafe { slice::from_raw_parts(ptr.cast::(), total_size) }; Self::ref_from_slice(slice) } @@ -431,6 +440,8 @@ impl DynSizedStructure { let t_dst_size = T::dst_len(self.header()); // Creates thin or fat pointer, depending on type. let t_ptr = ptr_meta::from_raw_parts(base_ptr.cast(), t_dst_size); + // SAFETY: `self` is a valid reference and the cast keeps the same + // allocation; `T::dst_len` determines the matching tail length. let t_ref = unsafe { &*t_ptr }; assert_eq!(size_of_val(self), size_of_val(t_ref)); diff --git a/multiboot2-common/src/tag.rs b/multiboot2-common/src/tag.rs index c655750b..b009fdb9 100644 --- a/multiboot2-common/src/tag.rs +++ b/multiboot2-common/src/tag.rs @@ -58,6 +58,8 @@ pub trait MaybeDynSized: Pointee { /// Returns the corresponding [`Header`]. fn header(&self) -> &Self::Header { let ptr = core::ptr::addr_of!(*self); + // SAFETY: `self` is a valid reference and `Self::Header` is the + // prefix of this `repr(C)` structure at the same address. unsafe { &*ptr.cast::() } } @@ -75,6 +77,8 @@ pub trait MaybeDynSized: Pointee { let ptr = core::ptr::addr_of!(*self); // Actual tag size with optional terminating padding. let size = size_of_val(self); + // SAFETY: `ptr` points to `self`'s allocation and `size_of_val(self)` + // covers the initialized object representation, including padding. let slice = unsafe { slice::from_raw_parts(ptr.cast::(), size) }; // Unwrap is fine as this type can't exist without the underlying memory // guarantees. diff --git a/multiboot2-header/src/builder.rs b/multiboot2-header/src/builder.rs index 0037c700..6dbf2087 100644 --- a/multiboot2-header/src/builder.rs +++ b/multiboot2-header/src/builder.rs @@ -215,6 +215,8 @@ mod tests { )); let structure = builder.build(); + // SAFETY: The builder emits a fully formed, aligned header + // buffer with a valid end tag. let header = unsafe { Multiboot2Header::load(structure.as_bytes().as_ref().as_ptr().cast()) } .unwrap(); diff --git a/multiboot2-header/src/header.rs b/multiboot2-header/src/header.rs index 15055e40..11d326cc 100644 --- a/multiboot2-header/src/header.rs +++ b/multiboot2-header/src/header.rs @@ -50,6 +50,8 @@ impl<'a> Multiboot2Header<'a> { /// program may observe unsynchronized mutation. pub unsafe fn load(ptr: *const Multiboot2BasicHeader) -> Result { let ptr = NonNull::new(ptr.cast_mut()).ok_or(LoadError::Memory(MemoryError::Null))?; + // SAFETY: `ptr` was checked for null and the DST constructor + // validates size and layout. let inner = unsafe { DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)? }; let this = Self(inner); @@ -163,6 +165,8 @@ impl<'a> Multiboot2Header<'a> { .wrapping_add(magic_begin_idx) .cast::(); + // SAFETY: `ptr` points into `buffer`, which has been checked + // for alignment and bounds. let header = unsafe { Self::load(ptr)? }; Ok((header, magic_begin_idx)) } @@ -170,7 +174,8 @@ impl<'a> Multiboot2Header<'a> { /// Returns a [`TagIter`]. #[must_use] pub fn iter(&self) -> TagIter<'_> { - // SAFETY: We validated the chain of tags beforehand. + // SAFETY: `load()` validated the tag chain, and the iterator + // only walks that validated payload. unsafe { TagIter::new(self.0.payload()) } } @@ -501,6 +506,8 @@ mod tests { let mut bytes = AlignedBytes::new([0; 24]); write_minimal_valid_header_tag(&mut bytes.0); + // SAFETY: The test buffer is aligned and contains a valid + // header layout. let header = unsafe { Multiboot2Header::load(bytes.as_ptr().cast()) }; assert!(header.is_ok()); @@ -514,6 +521,8 @@ mod tests { bytes.0[8..12].copy_from_slice(&16_u32.to_le_bytes()); bytes.0[12..16].copy_from_slice(&checksum.to_le_bytes()); + // SAFETY: The test buffer is aligned and contains a valid + // header layout. let header = unsafe { Multiboot2Header::load(bytes.as_ptr().cast()) }; assert!(matches!(header, Err(LoadError::NoEndTag))); @@ -530,6 +539,8 @@ mod tests { bytes.0[16..18].copy_from_slice(&(HeaderTagType::InformationRequest as u16).to_le_bytes()); bytes.0[20..24].copy_from_slice(&24_u32.to_le_bytes()); + // SAFETY: The test buffer is aligned and contains a valid + // header layout. let header = unsafe { Multiboot2Header::load(bytes.as_ptr().cast()) }; assert_eq!( diff --git a/multiboot2-header/src/information_request.rs b/multiboot2-header/src/information_request.rs index 2e5f9e20..8d76012e 100644 --- a/multiboot2-header/src/information_request.rs +++ b/multiboot2-header/src/information_request.rs @@ -26,6 +26,7 @@ impl InformationRequestHeaderTag { #[must_use] pub fn new(flags: HeaderTagFlag, requests: &[MbiTagTypeId]) -> Box { let header = HeaderTagHeader::new(HeaderTagType::InformationRequest, flags, 0); + // SAFETY: The memory we are using is valid. let requests = unsafe { let ptr = ptr::addr_of!(*requests); slice::from_raw_parts(ptr.cast::(), size_of_val(requests)) diff --git a/multiboot2-header/src/lib.rs b/multiboot2-header/src/lib.rs index 7ed1d60e..8ca54871 100644 --- a/multiboot2-header/src/lib.rs +++ b/multiboot2-header/src/lib.rs @@ -31,6 +31,7 @@ clippy::all, clippy::cargo, clippy::nursery, + clippy::undocumented_unsafe_blocks, clippy::must_use_candidate, // clippy::restriction, // clippy::pedantic diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index f9097523..ba4f114d 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -103,6 +103,8 @@ impl<'a> BootInformation<'a> { /// program may observe unsynchronized mutation. pub unsafe fn load(ptr: *const BootInformationHeader) -> Result { let ptr = NonNull::new(ptr.cast_mut()).ok_or(LoadError::Memory(MemoryError::Null))?; + // SAFETY: `ptr` was checked for null and `ref_from_ptr` validates the + // reported total size before constructing the DST reference. let inner = unsafe { DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)? }; let this = Self(inner); diff --git a/multiboot2/src/builder.rs b/multiboot2/src/builder.rs index bdf61c99..3d4ef12e 100644 --- a/multiboot2/src/builder.rs +++ b/multiboot2/src/builder.rs @@ -372,6 +372,8 @@ mod tests { let structure = builder.build(); + // SAFETY: The builder constructs a complete, aligned MBI with + // a valid end tag. let info = unsafe { BootInformation::load(structure.as_bytes().as_ptr().cast()) }.unwrap(); for tag in info.tags() { // Mainly a test for Miri. diff --git a/multiboot2/src/end.rs b/multiboot2/src/end.rs index c7099bc1..a9bb7faa 100644 --- a/multiboot2/src/end.rs +++ b/multiboot2/src/end.rs @@ -38,6 +38,8 @@ mod tests { #[test] /// Compile time test for [`EndTag`]. fn test_end_tag_size() { + // SAFETY: `EndTag` is a plain `repr(C)` POD with the exact + // eight-byte end-tag layout. unsafe { transmute::<[u8; 8], EndTag>([0u8; 8]); } diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index 4d5eca46..9c4d4ff7 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -52,7 +52,7 @@ impl<'a> Reader<'a> { } const fn current_ptr(&self) -> *const u8 { - unsafe { self.buffer.as_ptr().add(self.off) } + self.buffer.as_ptr().wrapping_add(self.off) } } @@ -187,7 +187,7 @@ impl FramebufferTag { self.buffer.len() - reader.off >= palette_len, "indexed framebuffer palette must fit in the tag" ); - + // SAFETY: The memory we are using is valid. unsafe { slice::from_raw_parts( reader.current_ptr().cast::(), diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 970686b0..82b76294 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -4,6 +4,7 @@ clippy::all, clippy::cargo, clippy::nursery, + clippy::undocumented_unsafe_blocks, clippy::must_use_candidate, // clippy::restriction, // clippy::pedantic @@ -145,6 +146,8 @@ mod tests { 8, 0, 0, 0, // end tag size ]); let ptr = bytes.0.as_ptr(); + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); @@ -162,6 +165,8 @@ mod tests { ]); let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); assert_eq!(addr, bi.start_address()); @@ -183,6 +188,8 @@ mod tests { 8, 0, 0, // end tag size ]); let ptr = bytes.0.as_ptr(); + // SAFETY: The buffer is aligned; the invalidity is in its + // contents. let bi = unsafe { BootInformation::load(ptr.cast()) }; assert_eq!(bi, Err(LoadError::Memory(MemoryError::MissingPadding))); @@ -197,6 +204,8 @@ mod tests { 9, 0, 0, 0, // end tag size ]); let ptr = bytes.0.as_ptr(); + // SAFETY: The buffer is aligned; the invalidity is in its + // contents. let bi = unsafe { BootInformation::load(ptr.cast()) }; assert_eq!( @@ -218,6 +227,8 @@ mod tests { 0, 0, 0, 0, // tag data ]); let ptr = bytes.0.as_ptr(); + // SAFETY: The buffer is aligned; the invalidity is in its + // contents. let bi = unsafe { BootInformation::load(ptr.cast()) }; assert_eq!( @@ -242,6 +253,8 @@ mod tests { ]); let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); assert_eq!(addr, bi.start_address()); @@ -283,6 +296,8 @@ mod tests { ]); let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a valid synthetic + // MBI. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); assert_eq!(addr, bi.start_address()); @@ -347,6 +362,8 @@ mod tests { ]); let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a valid synthetic + // MBI. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); assert_eq!(addr, bi.start_address()); @@ -462,6 +479,8 @@ mod tests { let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a valid synthetic + // MBI. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); assert_eq!(addr, bi.start_address()); @@ -553,6 +572,7 @@ mod tests { #[test] /// Compile time test for [`VBEInfoTag`]. fn vbe_info_tag_size() { + // SAFETY: The test only checks the fixed-size ABI layout. unsafe { // 16 for the start + 512 from `VBEControlInfo` + 256 from `VBEModeInfo`. transmute::<[u8; 784], VBEInfoTag>([0u8; 784]); @@ -819,6 +839,8 @@ mod tests { } let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); test_grub2_boot_info(&bi, addr, string_addr, &bytes.0, &string_bytes.0); @@ -1074,6 +1096,8 @@ mod tests { } let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); @@ -1144,6 +1168,8 @@ mod tests { ]); let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); assert_eq!(addr, bi.start_address()); @@ -1177,6 +1203,8 @@ mod tests { 0, 0, 0, 0, // end tag type. 8, 0, 0, 0, // end tag size. ]); + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(bytes2.as_ptr().cast()) }; let bi = bi.unwrap(); let efi_mmap = bi.efi_memory_map_tag(); @@ -1249,6 +1277,8 @@ mod tests { ]); let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); assert_eq!(addr, bi.start_address()); @@ -1332,6 +1362,8 @@ mod tests { ]); let ptr = bytes.0.as_ptr(); let addr = ptr as usize; + // SAFETY: The buffer is aligned and contains a complete + // synthetic MBI with a valid end tag. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); assert_eq!(addr, bi.start_address()); @@ -1381,6 +1413,8 @@ mod tests { ]); let ptr = bytes.0.as_ptr(); + // SAFETY: The buffer is aligned and contains a valid synthetic + // MBI. let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index eda47e26..f320e659 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -43,6 +43,8 @@ impl MemoryMapTag { let areas = { let ptr = areas.as_ptr().cast::(); let len = size_of_val(areas); + // SAFETY: `areas` is a live slice; we only reinterpret its + // initialized bytes. unsafe { slice::from_raw_parts(ptr, len) } }; new_boxed(header, &[&entry_size, &entry_version, areas]) @@ -334,6 +336,8 @@ impl EFIMemoryMapTag { let efi_mmap = { let ptr = descs.as_ptr().cast::(); let len = size_of_val(descs); + // SAFETY: `descs` is a live slice; we only reinterpret its + // initialized bytes. unsafe { slice::from_raw_parts(ptr, len) } }; @@ -451,14 +455,16 @@ impl<'a> Iterator for EFIMemoryAreaIter<'a> { return None; } - let desc = unsafe { - self.mmap_tag + let desc = { + let ptr = self + .mmap_tag .memory_map .as_ptr() - .add(self.i * self.mmap_tag.desc_size as usize) - .cast::() - .as_ref() - .unwrap() + .wrapping_add(self.i * self.mmap_tag.desc_size as usize) + .cast::(); + // SAFETY: The enclosing tag bounds the iterator, and `ptr` + // points into it. + unsafe { ptr.as_ref().unwrap() } }; self.i += 1; @@ -560,8 +566,10 @@ mod tests { 10, 8433664, 0, 1, 15, 0, 7, 8437760, 0, 4, 15, 0, 10, 8454144, 0, 240, 15, 0, ]; let buf = MMAP_RAW; + // SAFETY: `buf` is a plain array; we only reinterpret its + // initialized bytes. let buf = unsafe { - core::slice::from_raw_parts(buf.as_ptr().cast::(), buf.len() * size_of::()) + slice::from_raw_parts(buf.as_ptr().cast::(), buf.len() * size_of::()) }; let tag = EFIMemoryMapTag::new_from_map(DESC_SIZE, DESC_VERSION, buf); let entries = tag.memory_areas().copied().collect::>(); diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index e47d8d86..d8d89361 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -62,6 +62,7 @@ impl RsdpV1Tag { /// Validation of the RSDPv1 checksum #[must_use] pub fn checksum_is_valid(&self) -> bool { + // SAFETY: The memory we are using is valid. let bytes = unsafe { slice::from_raw_parts(self as *const _ as *const u8, RSDPV1_LENGTH + 8) }; bytes[8..] @@ -159,6 +160,7 @@ impl RsdpV2Tag { /// Validation of the RSDPv2 extended checksum #[must_use] pub fn checksum_is_valid(&self) -> bool { + // SAFETY: The memory we are using is valid. let bytes = unsafe { slice::from_raw_parts(self as *const _ as *const u8, self.length as usize + 8) }; From 8ed1c86d0ed42cd8124b686d18e4a5b99754e2bf Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 22 Jun 2026 15:22:46 +0200 Subject: [PATCH 05/10] multiboot2: auto-compute RSDP checksums Make the RSDP constructors fill in the checksum fields themselves so callers do not have to pass them in, and keep the builder tests matched to that API change. Validation now uses fixed-size slices for RSDP v1 and rejects bogus v2 lengths before slicing, which removes the previous out-of-bounds read risk from checksum validation. --- multiboot2/CHANGELOG.md | 2 + multiboot2/src/builder.rs | 4 +- multiboot2/src/rsdp.rs | 135 +++++++++++++++++++++++++++++++------- 3 files changed, 116 insertions(+), 25 deletions(-) diff --git a/multiboot2/CHANGELOG.md b/multiboot2/CHANGELOG.md index 6dad733c..9933cc6e 100644 --- a/multiboot2/CHANGELOG.md +++ b/multiboot2/CHANGELOG.md @@ -13,6 +13,8 @@ - Added UserDefined section to `ElfSectionType`. - Added equality implementations for `BootInformation`. - Fixed `BootInformation::load` to validate the complete padded tag sequence. +- Fixed RSDP tag constructors to compute their checksums automatically and + tightened checksum validation to avoid reading past malformed lengths. - Fixed indexed framebuffer parsing to reject palette metadata that exceeds the tag payload. - Fixed EFI memory map parsing to reject descriptor sizes that cannot safely diff --git a/multiboot2/src/builder.rs b/multiboot2/src/builder.rs index 3d4ef12e..b34c5357 100644 --- a/multiboot2/src/builder.rs +++ b/multiboot2/src/builder.rs @@ -354,8 +354,8 @@ mod tests { .efi64(EFISdt64Tag::new(0x1000)) .add_smbios(SmbiosTag::new(0, 0, &[1, 2, 3])) .add_smbios(SmbiosTag::new(1, 1, &[4, 5, 6])) - .rsdpv1(RsdpV1Tag::new(0, *b"abcdef", 5, 6)) - .rsdpv2(RsdpV2Tag::new(0, *b"abcdef", 5, 6, 5, 4, 7)) + .rsdpv1(RsdpV1Tag::new(*b"abcdef", 5, 6)) + .rsdpv2(RsdpV2Tag::new(*b"abcdef", 5, 6, 5, 4)) .efi_mmap(EFIMemoryMapTag::new_from_descs(&[ MemoryDescriptor::default(), MemoryDescriptor::default(), diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index d8d89361..da9ce357 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -19,7 +19,16 @@ use core::str; use core::str::Utf8Error; use multiboot2_common::{MaybeDynSized, Tag}; -const RSDPV1_LENGTH: usize = 20; +fn sum_bytes(parts: &[&[u8]]) -> u8 { + parts + .iter() + .flat_map(|part| part.iter().copied()) + .fold(0u8, |acc, val| acc.wrapping_add(val)) +} + +fn compute_checksum(bytes: &[&[u8]]) -> u8 { + 0u8.wrapping_sub(sum_bytes(bytes)) +} /// This tag contains a copy of RSDP as defined per ACPI 1.0 specification. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -30,7 +39,8 @@ pub struct RsdpV1Tag { checksum: u8, oem_id: [u8; 6], revision: u8, - rsdt_address: u32, // This is the PHYSICAL address of the RSDT + // This is the PHYSICAL address of the RSDT + rsdt_address: u32, } impl RsdpV1Tag { @@ -41,7 +51,17 @@ impl RsdpV1Tag { /// Constructs a new tag. #[must_use] - pub fn new(checksum: u8, oem_id: [u8; 6], revision: u8, rsdt_address: u32) -> Self { + pub fn new(oem_id: [u8; 6], revision: u8, rsdt_address: u32) -> Self { + let rsdt_address_bytes = rsdt_address.to_le_bytes(); + let checksum_seed = [0u8]; + let revision_bytes = [revision]; + let checksum = compute_checksum(&[ + Self::SIGNATURE.as_slice(), + checksum_seed.as_slice(), + oem_id.as_slice(), + revision_bytes.as_slice(), + rsdt_address_bytes.as_slice(), + ]); Self { header: TagHeader::new(Self::ID, Self::BASE_SIZE as u32), signature: Self::SIGNATURE, @@ -59,16 +79,18 @@ impl RsdpV1Tag { str::from_utf8(&self.signature) } - /// Validation of the RSDPv1 checksum + /// Validation of the RSDPv1 checksum. #[must_use] pub fn checksum_is_valid(&self) -> bool { - // SAFETY: The memory we are using is valid. - let bytes = - unsafe { slice::from_raw_parts(self as *const _ as *const u8, RSDPV1_LENGTH + 8) }; - bytes[8..] - .iter() - .fold(0u8, |acc, val| acc.wrapping_add(*val)) - == 0 + let rsdp_ptr = (self as *const Self) + .cast::() + // Skip header + .wrapping_add(size_of::()); + let rsdp_len = Self::BASE_SIZE - size_of::(); + // SAFETY: `self` is a valid reference, and we only read the + // initialized raw representation of the fixed-size RSDP payload. + let bytes = unsafe { slice::from_raw_parts(rsdp_ptr, rsdp_len) }; + sum_bytes(&[bytes]) == 0 } /// An OEM-supplied string that identifies the OEM. @@ -112,8 +134,8 @@ pub struct RsdpV2Tag { revision: u8, rsdt_address: u32, length: u32, - xsdt_address: u64, // This is the PHYSICAL address of the XSDT + xsdt_address: u64, ext_checksum: u8, _reserved: [u8; 3], } @@ -128,14 +150,37 @@ impl RsdpV2Tag { /// Constructs a new tag. #[must_use] pub fn new( - checksum: u8, oem_id: [u8; 6], revision: u8, rsdt_address: u32, length: u32, xsdt_address: u64, - ext_checksum: u8, ) -> Self { + let rsdt_address_bytes = rsdt_address.to_le_bytes(); + let length_bytes = length.to_le_bytes(); + let xsdt_address_bytes = xsdt_address.to_le_bytes(); + let checksum_seed = [0u8]; + let ext_checksum_seed = [0u8]; + let reserved = [0u8; 3]; + let revision_bytes = [revision]; + let checksum = compute_checksum(&[ + Self::SIGNATURE.as_slice(), + checksum_seed.as_slice(), + oem_id.as_slice(), + revision_bytes.as_slice(), + rsdt_address_bytes.as_slice(), + ]); + let ext_checksum = compute_checksum(&[ + Self::SIGNATURE.as_slice(), + core::slice::from_ref(&checksum), + oem_id.as_slice(), + revision_bytes.as_slice(), + rsdt_address_bytes.as_slice(), + length_bytes.as_slice(), + xsdt_address_bytes.as_slice(), + ext_checksum_seed.as_slice(), + reserved.as_slice(), + ]); Self { header: TagHeader::new(Self::ID, Self::BASE_SIZE as u32), signature: Self::SIGNATURE, @@ -157,17 +202,23 @@ impl RsdpV2Tag { str::from_utf8(&self.signature) } - /// Validation of the RSDPv2 extended checksum + /// Validation of the RSDPv2 extended checksum. #[must_use] pub fn checksum_is_valid(&self) -> bool { - // SAFETY: The memory we are using is valid. - let bytes = unsafe { - slice::from_raw_parts(self as *const _ as *const u8, self.length as usize + 8) - }; - bytes[8..] - .iter() - .fold(0u8, |acc, val| acc.wrapping_add(*val)) - == 0 + // SAFETY: `self` is a valid reference, and we only read the + // initialized raw representation of the fixed-size layout. + let bytes = + unsafe { slice::from_raw_parts((self as *const Self).cast::(), size_of::()) }; + let length = self.length as usize; + if length != Self::BASE_SIZE - size_of::() { + return false; + } + let ext_end = size_of::() + length; + if ext_end > bytes.len() { + return false; + } + + sum_bytes(&[&bytes[8..28]]) == 0 && sum_bytes(&[&bytes[8..ext_end]]) == 0 } /// An OEM-supplied string that identifies the OEM. @@ -207,3 +258,41 @@ impl Tag for RsdpV2Tag { const ID: TagType = TagType::AcpiV2; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn v1_new_computes_valid_checksum() { + let tag = RsdpV1Tag::new(*b"ABCDEF", 1, 0x1234_5678); + assert!(tag.checksum_is_valid()); + } + + #[test] + fn v2_new_computes_valid_checksums() { + let tag = RsdpV2Tag::new(*b"ABCDEF", 2, 0x1234_5678, 36, 0x1234_5678_9abc_def0); + assert!(tag.checksum_is_valid()); + } + + #[test] + fn v2_checksum_validation_rejects_corruption() { + let mut tag = RsdpV2Tag::new(*b"ABCDEF", 2, 0x1234_5678, 36, 0x1234_5678_9abc_def0); + tag.ext_checksum ^= 1; + assert!(!tag.checksum_is_valid()); + } + + #[test] + fn v2_checksum_validation_rejects_checksum_corruption() { + let mut tag = RsdpV2Tag::new(*b"ABCDEF", 2, 0x1234_5678, 36, 0x1234_5678_9abc_def0); + tag.checksum ^= 1; + assert!(!tag.checksum_is_valid()); + } + + #[test] + fn v2_checksum_validation_rejects_invalid_length() { + let mut tag = RsdpV2Tag::new(*b"ABCDEF", 2, 0x1234_5678, 36, 0x1234_5678_9abc_def0); + tag.length = 0; + assert!(!tag.checksum_is_valid()); + } +} From 97ba818d07790daf89b81e7cb679d8d5ed9b96fb Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 23 Jun 2026 09:25:11 +0200 Subject: [PATCH 06/10] multiboot2: remove warning --- multiboot2/src/builder.rs | 6 +++--- multiboot2/src/lib.rs | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/multiboot2/src/builder.rs b/multiboot2/src/builder.rs index b34c5357..51efa95e 100644 --- a/multiboot2/src/builder.rs +++ b/multiboot2/src/builder.rs @@ -53,7 +53,7 @@ impl Builder { Self { cmdline: None, bootloader: None, - modules: vec![], + modules: alloc::vec![], meminfo: None, bootdev: None, mmap: None, @@ -63,7 +63,7 @@ impl Builder { apm: None, efi32: None, efi64: None, - smbios: vec![], + smbios: alloc::vec![], rsdpv1: None, rsdpv2: None, efi_mmap: None, @@ -72,7 +72,7 @@ impl Builder { efi32_ih: None, efi64_ih: None, image_load_addr: None, - custom_tags: vec![], + custom_tags: alloc::vec![], } } diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 82b76294..908d15ad 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -44,7 +44,6 @@ //! ## MSRV //! The MSRV is 1.85.1 stable. -#[cfg_attr(feature = "builder", macro_use)] #[cfg(feature = "builder")] extern crate alloc; From 30dc253ad7a7b6aa625adfc7a1ef3a5043c541b5 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 23 Jun 2026 09:39:14 +0200 Subject: [PATCH 07/10] treewide: replace addr_of!() macro with new raw pointer syntax --- .../bins/multiboot2_chainloader/src/main.rs | 3 --- .../bins/multiboot2_chainloader/src/multiboot.rs | 3 +-- multiboot2-common/src/boxed.rs | 2 +- multiboot2-common/src/lib.rs | 3 +-- multiboot2-common/src/tag.rs | 4 ++-- multiboot2-common/src/test_utils.rs | 14 ++++++-------- multiboot2-header/src/information_request.rs | 7 ++----- multiboot2/src/boot_information.rs | 2 +- 8 files changed, 14 insertions(+), 24 deletions(-) diff --git a/integration-test/bins/multiboot2_chainloader/src/main.rs b/integration-test/bins/multiboot2_chainloader/src/main.rs index 81a0cca0..8f4fe640 100644 --- a/integration-test/bins/multiboot2_chainloader/src/main.rs +++ b/integration-test/bins/multiboot2_chainloader/src/main.rs @@ -6,9 +6,6 @@ mod multiboot; extern crate alloc; -#[macro_use] -extern crate integration_test_util; - use integration_test_util::init_environment; core::arch::global_asm!(include_str!("start.S"), options(att_syntax)); diff --git a/integration-test/bins/multiboot2_chainloader/src/multiboot.rs b/integration-test/bins/multiboot2_chainloader/src/multiboot.rs index 38b2e934..1ee0b41b 100644 --- a/integration-test/bins/multiboot2_chainloader/src/multiboot.rs +++ b/integration-test/bins/multiboot2_chainloader/src/multiboot.rs @@ -1,7 +1,6 @@ //! Parsing the Multiboot information. Glue code for the [`multiboot`] code. use anyhow::anyhow; -use core::ptr::addr_of_mut; use core::slice; use multiboot::information::{MemoryManagement, Multiboot, PAddr, SIGNATURE_EAX}; @@ -13,7 +12,7 @@ pub fn get_mbi<'a>(magic: u32, ptr: u32) -> anyhow::Result + ?Sized>( // write header { let len = size_of::(); - let ptr = core::ptr::addr_of!(header); + let ptr = &raw const header; // SAFETY: `header` is a fully initialized stack value and `heap_ptr` // points into the freshly allocated destination buffer. unsafe { diff --git a/multiboot2-common/src/lib.rs b/multiboot2-common/src/lib.rs index 669f2c64..04492b40 100644 --- a/multiboot2-common/src/lib.rs +++ b/multiboot2-common/src/lib.rs @@ -257,7 +257,6 @@ pub use iter::TagIter; pub use tag::{MaybeDynSized, Tag}; use core::fmt::Debug; -use core::ptr; use core::ptr::NonNull; use core::slice; use thiserror::Error; @@ -431,7 +430,7 @@ impl DynSizedStructure { { // Thin or fat pointer, depending on type. // However, only thin ptr is needed. - let base_ptr = ptr::addr_of!(*self); + let base_ptr = &raw const *self; // This should be a compile-time assertion. However, this is the best // location to place it for now. diff --git a/multiboot2-common/src/tag.rs b/multiboot2-common/src/tag.rs index b009fdb9..6f21a4a3 100644 --- a/multiboot2-common/src/tag.rs +++ b/multiboot2-common/src/tag.rs @@ -57,7 +57,7 @@ pub trait MaybeDynSized: Pointee { /// Returns the corresponding [`Header`]. fn header(&self) -> &Self::Header { - let ptr = core::ptr::addr_of!(*self); + let ptr = &raw const *self; // SAFETY: `self` is a valid reference and `Self::Header` is the // prefix of this `repr(C)` structure at the same address. unsafe { &*ptr.cast::() } @@ -74,7 +74,7 @@ pub trait MaybeDynSized: Pointee { /// [`BytesRef`]. This includes padding bytes. To only get the "true" tag /// data, read the tag size from [`Self::header`] and create a sub slice. fn as_bytes(&self) -> BytesRef<'_, Self::Header> { - let ptr = core::ptr::addr_of!(*self); + let ptr = &raw const *self; // Actual tag size with optional terminating padding. let size = size_of_val(self); // SAFETY: `ptr` points to `self`'s allocation and `size_of_val(self)` diff --git a/multiboot2-common/src/test_utils.rs b/multiboot2-common/src/test_utils.rs index 702568a3..c534eefc 100644 --- a/multiboot2-common/src/test_utils.rs +++ b/multiboot2-common/src/test_utils.rs @@ -116,8 +116,6 @@ impl Tag for DummyDstTag { #[cfg(test)] mod tests { - use core::ptr::addr_of; - use crate::ALIGNMENT; use super::*; @@ -128,17 +126,17 @@ mod tests { let bytes = AlignedBytes([0]); assert_eq!(bytes.as_ptr().align_offset(8), 0); - assert_eq!((addr_of!(bytes[0])).align_offset(8), 0); + assert_eq!((&raw const bytes[0]).align_offset(8), 0); let bytes = AlignedBytes([0, 1, 2, 3, 4, 5, 6, 7]); assert_eq!(bytes.as_ptr().align_offset(8), 0); - assert_eq!((addr_of!(bytes[0])).align_offset(8), 0); + assert_eq!((&raw const bytes[0]).align_offset(8), 0); let bytes = AlignedBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); assert_eq!(bytes.as_ptr().align_offset(8), 0); - assert_eq!((addr_of!(bytes[0])).align_offset(8), 0); - assert_eq!((addr_of!(bytes[7])).align_offset(8), 1); - assert_eq!((addr_of!(bytes[8])).align_offset(8), 0); - assert_eq!((addr_of!(bytes[9])).align_offset(8), 7); + assert_eq!((&raw const bytes[0]).align_offset(8), 0); + assert_eq!((&raw const bytes[7]).align_offset(8), 1); + assert_eq!((&raw const bytes[8]).align_offset(8), 0); + assert_eq!((&raw const bytes[9]).align_offset(8), 7); } } diff --git a/multiboot2-header/src/information_request.rs b/multiboot2-header/src/information_request.rs index 8d76012e..03c091d8 100644 --- a/multiboot2-header/src/information_request.rs +++ b/multiboot2-header/src/information_request.rs @@ -6,10 +6,7 @@ use core::fmt::{Debug, Formatter}; use multiboot2_common::new_boxed; use multiboot2_common::{MaybeDynSized, Tag}; #[cfg(feature = "builder")] -use { - alloc::boxed::Box, - core::{ptr, slice}, -}; +use {alloc::boxed::Box, core::slice}; /// Specifies what specific tag types the bootloader should provide /// inside the mbi. @@ -28,7 +25,7 @@ impl InformationRequestHeaderTag { let header = HeaderTagHeader::new(HeaderTagType::InformationRequest, flags, 0); // SAFETY: The memory we are using is valid. let requests = unsafe { - let ptr = ptr::addr_of!(*requests); + let ptr = &raw const *requests; slice::from_raw_parts(ptr.cast::(), size_of_val(requests)) }; new_boxed(header, &[requests]) diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index ba4f114d..1ab8fbf1 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -134,7 +134,7 @@ impl<'a> BootInformation<'a> { /// Get the start address of the boot info as pointer. #[must_use] pub const fn as_ptr(&self) -> *const () { - core::ptr::addr_of!(*self.0).cast() + (&raw const *self.0).cast() } /// Get the end address of the boot info. From dcb05ad32a4fac3c7294f37dafb47f62e5c9e1be Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 23 Jun 2026 10:12:04 +0200 Subject: [PATCH 08/10] chore: update MSRV to 1.87 1.85 and 1.86 have a bug in clippy that let CI fail, although the code is valid. --- .github/workflows/rust.yml | 8 +++---- Cargo.toml | 2 +- multiboot2-common/CHANGELOG.md | 6 +---- multiboot2-common/README.md | 2 +- multiboot2-header/CHANGELOG.md | 13 +--------- multiboot2-header/README.md | 2 +- multiboot2-header/src/lib.rs | 2 +- multiboot2/CHANGELOG.md | 43 +++++++--------------------------- multiboot2/README.md | 2 +- multiboot2/src/lib.rs | 2 +- 10 files changed, 21 insertions(+), 61 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index cd6f92a2..34244ab5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -7,7 +7,7 @@ # and that it is as stable as possible. name: "Cargo workspace" # Run on every push (tag, branch) and pull_request -on: [pull_request, push, workflow_dispatch, merge_group] +on: [ pull_request, push, workflow_dispatch, merge_group ] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }} cancel-in-progress: true @@ -19,7 +19,7 @@ jobs: name: build (msrv) uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.85.1 # MSRV + rust-version: 1.87.0 # MSRV do-style-check: false features: builder build_stable: @@ -42,7 +42,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.85.1 # MSRV + rust-version: 1.87.0 # MSRV do-style-check: false rust-target: thumbv7em-none-eabihf features: builder @@ -94,7 +94,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.85.1 # MSRV + rust-version: 1.87.0 # MSRV do-style-check: true do-test: false features: builder diff --git a/Cargo.toml b/Cargo.toml index ebda4961..41cb59fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ default-members = [ "multiboot2-common", "multiboot2-header", ] -package.rust-version = "1.85.1" +package.rust-version = "1.87.0" package.edition = "2024" package.license = "MIT/Apache-2.0" diff --git a/multiboot2-common/CHANGELOG.md b/multiboot2-common/CHANGELOG.md index 2de83395..289cf423 100644 --- a/multiboot2-common/CHANGELOG.md +++ b/multiboot2-common/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- **Breaking:** The MSRV is now 1.87.0 - **Breaking:** `Header` now requires `total_size()` and derives `payload_len()` from it. - Added validation for complete padded tag sequences. @@ -10,31 +11,26 @@ exceeds the available buffer. - Small code improvements - ## v0.3.0 (2025-06-01) - **Breaking:** Removed the optional `unstable` feature (required nightly) - `core::error::Error` is now implemented unconditionally - **Breaking:** The MSRV is now 1.85 - ## v0.2.1 (2024-09-19) - Documentation improvements - ## v0.2.0 (2024-09-17) - dependency updates - **Breaking:** MSRV is now 1.75 - misc metadata fixes - ## v0.1.2 (2024-08-24) - Documentation improvements - ## 0.1.0 / 0.1.1 (2024-08-20) Initial release. diff --git a/multiboot2-common/README.md b/multiboot2-common/README.md index 6de15f03..9638aed4 100644 --- a/multiboot2-common/README.md +++ b/multiboot2-common/README.md @@ -46,7 +46,7 @@ to first study the inner box (`multiboot2-common`) and then study how types from ## MSRV -The MSRV is 1.85.1 stable. +The MSRV is 1.87.0 stable. ## License & Contribution diff --git a/multiboot2-header/CHANGELOG.md b/multiboot2-header/CHANGELOG.md index 2dd71c24..35c1f9e9 100644 --- a/multiboot2-header/CHANGELOG.md +++ b/multiboot2-header/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- **Breaking:** The MSRV is now 1.87.0 - Fixed `Multiboot2Header::load` to validate the complete padded tag sequence. - Changed `Multiboot2Header::find_header` to scan the full 32 KiB search window, validate candidate headers, and return the parsed header plus offset. @@ -12,26 +13,22 @@ the mandatory end tag. - Small code improvements - ## v0.7.0 (2025-06-01) - **Breaking:** Removed the optional `unstable` feature (required nightly) - `core::error::Error` is now implemented unconditionally - **Breaking:** The MSRV is now 1.85 - ## v0.6.0 (2024-09-17) - dependency updates - **Breaking:** MSRV is now 1.75 - misc metadata fixes - ## v0.5.1 (2024-08-24) - Documentation improvements - ## v0.5.0 (2024-05-20) This release contains a major refactoring of the internals, guaranteeing @@ -53,14 +50,12 @@ All previous versions have been marked as **YANKED**. `0.5.0` is the first version where all unit tests are passed by Miri, i.e., the first version without Undefined Behavior. - ## 0.4.0 (2024-05-01) (**YANKED**) - added `EndHeaderTag::default()` - MSRV is 1.70 - Can add multiple `TagType::Smbios` tags in the builder. - ## 0.3.2 (2023-11-30) (**YANKED**) - **BREAKING** bumped `multiboot2` dependency to `v0.19.0` @@ -68,12 +63,10 @@ without Undefined Behavior. anymore - doc update - ## 0.3.1 (2023-06-28) (**YANKED**) - doc update - ## 0.3.0 (2023-06-23) (**YANKED**) - **BREAKING** MSRV is 1.68.0 (UPDATE: This is actually 1.69.) @@ -87,7 +80,6 @@ without Undefined Behavior. - added the optional `unstable` feature (requires nightly) - implement `core::error::Error` for `LoadError` - ## 0.2.0 (2022-05-03) (**YANKED**) - **BREAKING** renamed `EntryHeaderTag` to `EntryAddressHeaderTag` @@ -96,7 +88,6 @@ without Undefined Behavior. -> thus, import paths are much more logically now - internal code improvements - ## 0.1.1 (2022-05-02) (**YANKED**) - fixed a bug that prevented the usage of the crate in `no_std` environments @@ -105,12 +96,10 @@ without Undefined Behavior. (this feature can be disabled which will also remove the dependency to the `alloc` crate) - ## 0.1.0 (2021-10-08) (**YANKED**) - initial release - ## 0.0.0 Empty release to save the name on crates.io diff --git a/multiboot2-header/README.md b/multiboot2-header/README.md index 9d7cef75..a3f36284 100644 --- a/multiboot2-header/README.md +++ b/multiboot2-header/README.md @@ -85,7 +85,7 @@ bytes of the ELF. See Multiboot2 specification. ## MSRV -The MSRV is 1.85.1 stable. +The MSRV is 1.87.0 stable. ## License & Contribution diff --git a/multiboot2-header/src/lib.rs b/multiboot2-header/src/lib.rs index 8ca54871..22329f89 100644 --- a/multiboot2-header/src/lib.rs +++ b/multiboot2-header/src/lib.rs @@ -23,7 +23,7 @@ //! //! ## MSRV //! -//! The MSRV is 1.85.1 stable. +//! The MSRV is 1.87.0 stable. #![no_std] // --- BEGIN STYLE CHECKS --- diff --git a/multiboot2/CHANGELOG.md b/multiboot2/CHANGELOG.md index 9933cc6e..8b915dc1 100644 --- a/multiboot2/CHANGELOG.md +++ b/multiboot2/CHANGELOG.md @@ -2,11 +2,16 @@ ## Unreleased +- **Breaking:** The MSRV is now 1.87.0 - **Breaking**: Renamed `VBEWindowAttributes::WRITABLE` (fix typo) -- **Breaking** Changed `multiboot2::elf_sections` to use the [elf](https://docs.rs/elf/latest/elf/) crate - - `ElfSectionsTag::sections()` now returns an iterator over `elf::section::SectionHeader`. - - `ElfSection` has been removed and replaced with `elf::section::SectionHeader`. -- Added `ElfSectionExt` trait to replace functionality for `ElfSection::flags()`, +- **Breaking** Changed `multiboot2::elf_sections` to use + the [elf](https://docs.rs/elf/latest/elf/) crate + - `ElfSectionsTag::sections()` now returns an iterator over + `elf::section::SectionHeader`. + - `ElfSection` has been removed and replaced with + `elf::section::SectionHeader`. +- Added `ElfSectionExt` trait to replace functionality for + `ElfSection::flags()`, `ElfSection::section_type()`, and `ElfSection::name()`. - Added `ElfSectionsTag::string_table()`. - Added some flags to `ElfSectionFlags`. @@ -33,7 +38,6 @@ - **Breaking:** The MSRV is now 1.85 - Fixed a bug causing UB in `ElfSection::name()` - ## v0.23.1 (2024-10-21) - Fix wrong tag ID when using `BootdevTag::new` @@ -42,7 +46,6 @@ `.sections()` to iterate the sections - Fixed the debug output of `BootInformation` - ## v0.23.0 (2024-09-17) - dependency updates @@ -54,18 +57,15 @@ - `BootInformation::tags` iterator is now public - misc metadata fixes - ## v0.22.2 (2024-08-24) - Documentation improvements - Improve debug formatting for EFIMemoryMapTag - ## v0.22.1 (2024-08-20) Minor documentation fixes. - ## v0.22.0 (2024-08-20) This release contains another major refactoring of the internals, guaranteeing @@ -104,7 +104,6 @@ All previous versions have been marked as **YANKED**. `0.22.0` is the first version where all unit tests are passed by Miri, i.e., the first version without Undefined Behavior. - ## 0.21.0 (2024-08-17) (**YANKED**) This release contains a massive refactoring of various internals. Now, almost @@ -136,12 +135,10 @@ release and you'll be fine!** - documentation enhancements - updated dependencies - ## 0.20.2 (2024-05-26) (**YANKED**) - fix Debug implementation of `EfiMemoryMapTag` - ## 0.20.1 (2024-05-26) (**YANKED**) - fixed the handling of `EFIMemoryMapTag` and `EFIMemoryAreaIter` @@ -150,13 +147,11 @@ release and you'll be fine!** `EFIMemoryMapTag::new_from_map`. - `ModuleTag::new`'s `end` parameter now must be bigger than `start`. - ## 0.20.0 (2024-05-01) (**YANKED**) - added `InformationBuilder::default()` - MSRV is 1.70 - ## 0.19.0 (2023-09-21) (**YANKED**) - **BREAKING** MSRV is 1.69.0 @@ -171,12 +166,10 @@ release and you'll be fine!** - `InformationBuilder` now also allows to add custom tags. The new public method `add_tag` was introduced for that. - ## 0.18.1 (2023-07-13) (**YANKED**) - Documentation improvements - ## 0.18.0 (2023-07-13) (**YANKED**) - **BREAKING** The `TagTrait` was enhanced and now has an associated `ID` @@ -198,7 +191,6 @@ release and you'll be fine!** - Better debug output of `BootInformation` and `MemoryArea` - Internal code cleanup. - ## 0.17.0 (2023-07-12) (**YANKED**) - **BREAKING** Make functions of `InformationBuilder` chainable. They now @@ -206,7 +198,6 @@ release and you'll be fine!** - **BREAKING** Allow non-standard memory area types by using new pair of corresponding types: `MemoryAreaTypeId` and `MemoryAreaType`. - ## 0.16.0 (2023-06-23) (**YANKED**) - **BREAKING** renamed `MULTIBOOT2_BOOTLOADER_MAGIC` to `MAGIC` @@ -245,7 +236,6 @@ release and you'll be fine!** - added `BootInformation::load` as new default constructor - added `MemoryMapTag::entry_size` and `MemoryMapTag::entry_version` - ## 0.15.1 (2023-03-18) (**YANKED**) - **BREAKING** `MemoryMapTag::all_memory_areas()` was renamed to `memory_areas` @@ -261,7 +251,6 @@ release and you'll be fine!** value. This prevents possible panics. - fix: prevent a possible panic in `ElfSection::section_type()` - ## 0.15.0 (2023-03-17) (**YANKED**) - **BREAKING** MSRV is 1.56.1 @@ -284,7 +273,6 @@ release and you'll be fine!** (check docs.rs). There is also a small unit test that you can use to learn from. - ## 0.14.2 (2023-03-17) (**YANKED**) - documentation fixes @@ -293,7 +281,6 @@ release and you'll be fine!** With this feature, `MbiLoadError` now implements `core::error::Error` and can be used with `anyhow::Result` for example. - ## 0.14.1 (2023-03-09) (**YANKED**) - fixed the calculation of the last area of the memory map @@ -301,7 +288,6 @@ release and you'll be fine!** (Previously, iterating the EFI Memory map resulted in a superfluous entry as it ran over the next tag) - ## 0.14.0 (2022-06-30) (**YANKED**) - **BREAKING CHANGES** \ @@ -318,30 +304,25 @@ release and you'll be fine!** - `RsdpV2Tag::oem_id` now returns a Result instead of an Option - internal code improvements - ## 0.13.3 (2022-06-03) (**YANKED**) - impl `Send` for `BootInformation` - ## 0.13.2 (2022-05-02) (**YANKED**) - `TagType` now implements `Ord` so that it can be used in `BTreeSet` - small internal improvements and restructuring of the code (no breaking changes to public API) - ## 0.13.1 (2022-01-09) (**YANKED**) - minor fix - ## 0.13.0 (2022-01-09) (**YANKED**) - added missing getters for tag `ImageLoadPhysAddr` - added missing getters for tags `EFIImageHandle32` and `EFIImageHandle64` - ## 0.12.2 (2021-10-02) (**YANKED**) - `TagType` now implements `Eq` and `Hash` @@ -355,13 +336,11 @@ release and you'll be fine!** - prepared co-existence of crates `multiboot2` and `multiboot2-header` in a Cargo workspace inside the same repository - ## 0.12.1 (2021-08-11) (**YANKED**) - `TagType`-enum introduced in `v0.11` is now actually public - internal code improvements - ## 0.12.0 (2021-08-06) (**YANKED**) - **breaking:** `load()` and `load_with_offset` now returns a result @@ -369,7 +348,6 @@ release and you'll be fine!** - Rust edition 2018 (instead of 2015) - internal code improvements - ## 0.11.0 (2021-07-07) (**YANKED**) - **breaking:** iterator functions (e.g. `ElfSectionsTag::sections()`) @@ -379,19 +357,16 @@ release and you'll be fine!** - much improved debug-formatting of `BootInformation` - internal code improvements / formatting - ## 0.10.0 (2020-11-03) (**YANKED**) - allow access to all memory regions (MemoryMap-Tag) - internal code improvements - ## 0.9.0 (2020-07-06) - Add a `checksum_is_valid` method to the RSDP tags ([#64](https://github.com/rust-osdev/multiboot2/pull/64)) - ## 0.8.2 (2022-03-02) - Add some basic diff --git a/multiboot2/README.md b/multiboot2/README.md index b5af7ac7..db577ce9 100644 --- a/multiboot2/README.md +++ b/multiboot2/README.md @@ -47,7 +47,7 @@ There are many different types of tags, but they all have the same beginning: ## MSRV -The MSRV is 1.85.1 stable. +The MSRV is 1.87.0 stable. ## License & Contribution diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 908d15ad..d5fb9831 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -42,7 +42,7 @@ //! ``` //! //! ## MSRV -//! The MSRV is 1.85.1 stable. +//! The MSRV is 1.87.0 stable. #[cfg(feature = "builder")] extern crate alloc; From 5a0598d3f5d3bf1301972174df0322533dc1ae27 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 23 Jun 2026 10:12:09 +0200 Subject: [PATCH 09/10] Revert "chore: update MSRV to 1.87" This reverts commit dcb05ad32a4fac3c7294f37dafb47f62e5c9e1be. --- .github/workflows/rust.yml | 8 +++---- Cargo.toml | 2 +- multiboot2-common/CHANGELOG.md | 6 ++++- multiboot2-common/README.md | 2 +- multiboot2-header/CHANGELOG.md | 13 +++++++++- multiboot2-header/README.md | 2 +- multiboot2-header/src/lib.rs | 2 +- multiboot2/CHANGELOG.md | 43 +++++++++++++++++++++++++++------- multiboot2/README.md | 2 +- multiboot2/src/lib.rs | 2 +- 10 files changed, 61 insertions(+), 21 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 34244ab5..cd6f92a2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -7,7 +7,7 @@ # and that it is as stable as possible. name: "Cargo workspace" # Run on every push (tag, branch) and pull_request -on: [ pull_request, push, workflow_dispatch, merge_group ] +on: [pull_request, push, workflow_dispatch, merge_group] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }} cancel-in-progress: true @@ -19,7 +19,7 @@ jobs: name: build (msrv) uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.87.0 # MSRV + rust-version: 1.85.1 # MSRV do-style-check: false features: builder build_stable: @@ -42,7 +42,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.87.0 # MSRV + rust-version: 1.85.1 # MSRV do-style-check: false rust-target: thumbv7em-none-eabihf features: builder @@ -94,7 +94,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.87.0 # MSRV + rust-version: 1.85.1 # MSRV do-style-check: true do-test: false features: builder diff --git a/Cargo.toml b/Cargo.toml index 41cb59fb..ebda4961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ default-members = [ "multiboot2-common", "multiboot2-header", ] -package.rust-version = "1.87.0" +package.rust-version = "1.85.1" package.edition = "2024" package.license = "MIT/Apache-2.0" diff --git a/multiboot2-common/CHANGELOG.md b/multiboot2-common/CHANGELOG.md index 289cf423..2de83395 100644 --- a/multiboot2-common/CHANGELOG.md +++ b/multiboot2-common/CHANGELOG.md @@ -2,7 +2,6 @@ ## Unreleased -- **Breaking:** The MSRV is now 1.87.0 - **Breaking:** `Header` now requires `total_size()` and derives `payload_len()` from it. - Added validation for complete padded tag sequences. @@ -11,26 +10,31 @@ exceeds the available buffer. - Small code improvements + ## v0.3.0 (2025-06-01) - **Breaking:** Removed the optional `unstable` feature (required nightly) - `core::error::Error` is now implemented unconditionally - **Breaking:** The MSRV is now 1.85 + ## v0.2.1 (2024-09-19) - Documentation improvements + ## v0.2.0 (2024-09-17) - dependency updates - **Breaking:** MSRV is now 1.75 - misc metadata fixes + ## v0.1.2 (2024-08-24) - Documentation improvements + ## 0.1.0 / 0.1.1 (2024-08-20) Initial release. diff --git a/multiboot2-common/README.md b/multiboot2-common/README.md index 9638aed4..6de15f03 100644 --- a/multiboot2-common/README.md +++ b/multiboot2-common/README.md @@ -46,7 +46,7 @@ to first study the inner box (`multiboot2-common`) and then study how types from ## MSRV -The MSRV is 1.87.0 stable. +The MSRV is 1.85.1 stable. ## License & Contribution diff --git a/multiboot2-header/CHANGELOG.md b/multiboot2-header/CHANGELOG.md index 35c1f9e9..2dd71c24 100644 --- a/multiboot2-header/CHANGELOG.md +++ b/multiboot2-header/CHANGELOG.md @@ -2,7 +2,6 @@ ## Unreleased -- **Breaking:** The MSRV is now 1.87.0 - Fixed `Multiboot2Header::load` to validate the complete padded tag sequence. - Changed `Multiboot2Header::find_header` to scan the full 32 KiB search window, validate candidate headers, and return the parsed header plus offset. @@ -13,22 +12,26 @@ the mandatory end tag. - Small code improvements + ## v0.7.0 (2025-06-01) - **Breaking:** Removed the optional `unstable` feature (required nightly) - `core::error::Error` is now implemented unconditionally - **Breaking:** The MSRV is now 1.85 + ## v0.6.0 (2024-09-17) - dependency updates - **Breaking:** MSRV is now 1.75 - misc metadata fixes + ## v0.5.1 (2024-08-24) - Documentation improvements + ## v0.5.0 (2024-05-20) This release contains a major refactoring of the internals, guaranteeing @@ -50,12 +53,14 @@ All previous versions have been marked as **YANKED**. `0.5.0` is the first version where all unit tests are passed by Miri, i.e., the first version without Undefined Behavior. + ## 0.4.0 (2024-05-01) (**YANKED**) - added `EndHeaderTag::default()` - MSRV is 1.70 - Can add multiple `TagType::Smbios` tags in the builder. + ## 0.3.2 (2023-11-30) (**YANKED**) - **BREAKING** bumped `multiboot2` dependency to `v0.19.0` @@ -63,10 +68,12 @@ without Undefined Behavior. anymore - doc update + ## 0.3.1 (2023-06-28) (**YANKED**) - doc update + ## 0.3.0 (2023-06-23) (**YANKED**) - **BREAKING** MSRV is 1.68.0 (UPDATE: This is actually 1.69.) @@ -80,6 +87,7 @@ without Undefined Behavior. - added the optional `unstable` feature (requires nightly) - implement `core::error::Error` for `LoadError` + ## 0.2.0 (2022-05-03) (**YANKED**) - **BREAKING** renamed `EntryHeaderTag` to `EntryAddressHeaderTag` @@ -88,6 +96,7 @@ without Undefined Behavior. -> thus, import paths are much more logically now - internal code improvements + ## 0.1.1 (2022-05-02) (**YANKED**) - fixed a bug that prevented the usage of the crate in `no_std` environments @@ -96,10 +105,12 @@ without Undefined Behavior. (this feature can be disabled which will also remove the dependency to the `alloc` crate) + ## 0.1.0 (2021-10-08) (**YANKED**) - initial release + ## 0.0.0 Empty release to save the name on crates.io diff --git a/multiboot2-header/README.md b/multiboot2-header/README.md index a3f36284..9d7cef75 100644 --- a/multiboot2-header/README.md +++ b/multiboot2-header/README.md @@ -85,7 +85,7 @@ bytes of the ELF. See Multiboot2 specification. ## MSRV -The MSRV is 1.87.0 stable. +The MSRV is 1.85.1 stable. ## License & Contribution diff --git a/multiboot2-header/src/lib.rs b/multiboot2-header/src/lib.rs index 22329f89..8ca54871 100644 --- a/multiboot2-header/src/lib.rs +++ b/multiboot2-header/src/lib.rs @@ -23,7 +23,7 @@ //! //! ## MSRV //! -//! The MSRV is 1.87.0 stable. +//! The MSRV is 1.85.1 stable. #![no_std] // --- BEGIN STYLE CHECKS --- diff --git a/multiboot2/CHANGELOG.md b/multiboot2/CHANGELOG.md index 8b915dc1..9933cc6e 100644 --- a/multiboot2/CHANGELOG.md +++ b/multiboot2/CHANGELOG.md @@ -2,16 +2,11 @@ ## Unreleased -- **Breaking:** The MSRV is now 1.87.0 - **Breaking**: Renamed `VBEWindowAttributes::WRITABLE` (fix typo) -- **Breaking** Changed `multiboot2::elf_sections` to use - the [elf](https://docs.rs/elf/latest/elf/) crate - - `ElfSectionsTag::sections()` now returns an iterator over - `elf::section::SectionHeader`. - - `ElfSection` has been removed and replaced with - `elf::section::SectionHeader`. -- Added `ElfSectionExt` trait to replace functionality for - `ElfSection::flags()`, +- **Breaking** Changed `multiboot2::elf_sections` to use the [elf](https://docs.rs/elf/latest/elf/) crate + - `ElfSectionsTag::sections()` now returns an iterator over `elf::section::SectionHeader`. + - `ElfSection` has been removed and replaced with `elf::section::SectionHeader`. +- Added `ElfSectionExt` trait to replace functionality for `ElfSection::flags()`, `ElfSection::section_type()`, and `ElfSection::name()`. - Added `ElfSectionsTag::string_table()`. - Added some flags to `ElfSectionFlags`. @@ -38,6 +33,7 @@ - **Breaking:** The MSRV is now 1.85 - Fixed a bug causing UB in `ElfSection::name()` + ## v0.23.1 (2024-10-21) - Fix wrong tag ID when using `BootdevTag::new` @@ -46,6 +42,7 @@ `.sections()` to iterate the sections - Fixed the debug output of `BootInformation` + ## v0.23.0 (2024-09-17) - dependency updates @@ -57,15 +54,18 @@ - `BootInformation::tags` iterator is now public - misc metadata fixes + ## v0.22.2 (2024-08-24) - Documentation improvements - Improve debug formatting for EFIMemoryMapTag + ## v0.22.1 (2024-08-20) Minor documentation fixes. + ## v0.22.0 (2024-08-20) This release contains another major refactoring of the internals, guaranteeing @@ -104,6 +104,7 @@ All previous versions have been marked as **YANKED**. `0.22.0` is the first version where all unit tests are passed by Miri, i.e., the first version without Undefined Behavior. + ## 0.21.0 (2024-08-17) (**YANKED**) This release contains a massive refactoring of various internals. Now, almost @@ -135,10 +136,12 @@ release and you'll be fine!** - documentation enhancements - updated dependencies + ## 0.20.2 (2024-05-26) (**YANKED**) - fix Debug implementation of `EfiMemoryMapTag` + ## 0.20.1 (2024-05-26) (**YANKED**) - fixed the handling of `EFIMemoryMapTag` and `EFIMemoryAreaIter` @@ -147,11 +150,13 @@ release and you'll be fine!** `EFIMemoryMapTag::new_from_map`. - `ModuleTag::new`'s `end` parameter now must be bigger than `start`. + ## 0.20.0 (2024-05-01) (**YANKED**) - added `InformationBuilder::default()` - MSRV is 1.70 + ## 0.19.0 (2023-09-21) (**YANKED**) - **BREAKING** MSRV is 1.69.0 @@ -166,10 +171,12 @@ release and you'll be fine!** - `InformationBuilder` now also allows to add custom tags. The new public method `add_tag` was introduced for that. + ## 0.18.1 (2023-07-13) (**YANKED**) - Documentation improvements + ## 0.18.0 (2023-07-13) (**YANKED**) - **BREAKING** The `TagTrait` was enhanced and now has an associated `ID` @@ -191,6 +198,7 @@ release and you'll be fine!** - Better debug output of `BootInformation` and `MemoryArea` - Internal code cleanup. + ## 0.17.0 (2023-07-12) (**YANKED**) - **BREAKING** Make functions of `InformationBuilder` chainable. They now @@ -198,6 +206,7 @@ release and you'll be fine!** - **BREAKING** Allow non-standard memory area types by using new pair of corresponding types: `MemoryAreaTypeId` and `MemoryAreaType`. + ## 0.16.0 (2023-06-23) (**YANKED**) - **BREAKING** renamed `MULTIBOOT2_BOOTLOADER_MAGIC` to `MAGIC` @@ -236,6 +245,7 @@ release and you'll be fine!** - added `BootInformation::load` as new default constructor - added `MemoryMapTag::entry_size` and `MemoryMapTag::entry_version` + ## 0.15.1 (2023-03-18) (**YANKED**) - **BREAKING** `MemoryMapTag::all_memory_areas()` was renamed to `memory_areas` @@ -251,6 +261,7 @@ release and you'll be fine!** value. This prevents possible panics. - fix: prevent a possible panic in `ElfSection::section_type()` + ## 0.15.0 (2023-03-17) (**YANKED**) - **BREAKING** MSRV is 1.56.1 @@ -273,6 +284,7 @@ release and you'll be fine!** (check docs.rs). There is also a small unit test that you can use to learn from. + ## 0.14.2 (2023-03-17) (**YANKED**) - documentation fixes @@ -281,6 +293,7 @@ release and you'll be fine!** With this feature, `MbiLoadError` now implements `core::error::Error` and can be used with `anyhow::Result` for example. + ## 0.14.1 (2023-03-09) (**YANKED**) - fixed the calculation of the last area of the memory map @@ -288,6 +301,7 @@ release and you'll be fine!** (Previously, iterating the EFI Memory map resulted in a superfluous entry as it ran over the next tag) + ## 0.14.0 (2022-06-30) (**YANKED**) - **BREAKING CHANGES** \ @@ -304,25 +318,30 @@ release and you'll be fine!** - `RsdpV2Tag::oem_id` now returns a Result instead of an Option - internal code improvements + ## 0.13.3 (2022-06-03) (**YANKED**) - impl `Send` for `BootInformation` + ## 0.13.2 (2022-05-02) (**YANKED**) - `TagType` now implements `Ord` so that it can be used in `BTreeSet` - small internal improvements and restructuring of the code (no breaking changes to public API) + ## 0.13.1 (2022-01-09) (**YANKED**) - minor fix + ## 0.13.0 (2022-01-09) (**YANKED**) - added missing getters for tag `ImageLoadPhysAddr` - added missing getters for tags `EFIImageHandle32` and `EFIImageHandle64` + ## 0.12.2 (2021-10-02) (**YANKED**) - `TagType` now implements `Eq` and `Hash` @@ -336,11 +355,13 @@ release and you'll be fine!** - prepared co-existence of crates `multiboot2` and `multiboot2-header` in a Cargo workspace inside the same repository + ## 0.12.1 (2021-08-11) (**YANKED**) - `TagType`-enum introduced in `v0.11` is now actually public - internal code improvements + ## 0.12.0 (2021-08-06) (**YANKED**) - **breaking:** `load()` and `load_with_offset` now returns a result @@ -348,6 +369,7 @@ release and you'll be fine!** - Rust edition 2018 (instead of 2015) - internal code improvements + ## 0.11.0 (2021-07-07) (**YANKED**) - **breaking:** iterator functions (e.g. `ElfSectionsTag::sections()`) @@ -357,16 +379,19 @@ release and you'll be fine!** - much improved debug-formatting of `BootInformation` - internal code improvements / formatting + ## 0.10.0 (2020-11-03) (**YANKED**) - allow access to all memory regions (MemoryMap-Tag) - internal code improvements + ## 0.9.0 (2020-07-06) - Add a `checksum_is_valid` method to the RSDP tags ([#64](https://github.com/rust-osdev/multiboot2/pull/64)) + ## 0.8.2 (2022-03-02) - Add some basic diff --git a/multiboot2/README.md b/multiboot2/README.md index db577ce9..b5af7ac7 100644 --- a/multiboot2/README.md +++ b/multiboot2/README.md @@ -47,7 +47,7 @@ There are many different types of tags, but they all have the same beginning: ## MSRV -The MSRV is 1.87.0 stable. +The MSRV is 1.85.1 stable. ## License & Contribution diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index d5fb9831..908d15ad 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -42,7 +42,7 @@ //! ``` //! //! ## MSRV -//! The MSRV is 1.87.0 stable. +//! The MSRV is 1.85.1 stable. #[cfg(feature = "builder")] extern crate alloc; From 8b5102708dc705c26e482fc038db4d9bcb4f4411 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 23 Jun 2026 10:13:17 +0200 Subject: [PATCH 10/10] multiboot2-header: workaround false-positive in clippy 1.85.1 This bug exists at least in clippy 1.85 and 1.86. 1.87 is fine. --- multiboot2-header/src/builder.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/multiboot2-header/src/builder.rs b/multiboot2-header/src/builder.rs index 6dbf2087..324f0467 100644 --- a/multiboot2-header/src/builder.rs +++ b/multiboot2-header/src/builder.rs @@ -215,11 +215,13 @@ mod tests { )); let structure = builder.build(); - // SAFETY: The builder emits a fully formed, aligned header - // buffer with a valid end tag. - let header = + + let header = { + // SAFETY: The builder emits a fully formed, aligned header + // buffer with a valid end tag. unsafe { Multiboot2Header::load(structure.as_bytes().as_ref().as_ptr().cast()) } - .unwrap(); + .unwrap() + }; assert_eq!(header.verify_checksum(), Ok(())); assert_eq!(