From 4a3d96ea33ed24e1580aac89c5296a86027e6020 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 23 Aug 2026 15:24:51 +0200 Subject: [PATCH 1/2] uefi-raw: replace ABI-related unit tests with consts checks This is best practice in the ecosystem and ensures that the ABI is correct for the target platform - which may be different from the test platform. --- uefi-raw/src/lib.rs | 14 ++++++++++---- uefi-raw/src/net.rs | 28 ++++++++++++++-------------- uefi-raw/src/protocol/device_path.rs | 20 +++++++------------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/uefi-raw/src/lib.rs b/uefi-raw/src/lib.rs index 1dad6966..b074b707 100644 --- a/uefi-raw/src/lib.rs +++ b/uefi-raw/src/lib.rs @@ -86,6 +86,14 @@ pub type VirtualAddress = u64; #[repr(transparent)] pub struct Boolean(pub u8); +// Ensure ABI guarantees for Boolean, as promised in [0]. +// +// [0]: https://github.com/tianocore/edk2/blob/b0f43dd3fdec2363e3548ec31eb455dc1c4ac761/MdePkg/Include/X64/ProcessorBind.h#L192 +const _: () = { + assert!(align_of::() == 1); + assert!(size_of::() == 1); +}; + impl Boolean { /// [`Boolean`] representing `true`. /// @@ -169,12 +177,10 @@ mod tests { use super::*; #[test] - /// Test the properties promised in [0]. This also applies for the other - /// architectures. + /// Test the properties promised in [0] and convenient rusty conversions. /// /// [0] https://github.com/tianocore/edk2/blob/b0f43dd3fdec2363e3548ec31eb455dc1c4ac761/MdePkg/Include/X64/ProcessorBind.h#L192 - fn test_boolean_abi() { - assert_eq!(size_of::(), 1); + fn test_boolean_conversions() { assert_eq!(Boolean::from(true).0, 1); assert_eq!(Boolean::from(false).0, 0); assert_eq!(Boolean::TRUE.0, 1); diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs index 53ea1d09..935d0c77 100644 --- a/uefi-raw/src/net.rs +++ b/uefi-raw/src/net.rs @@ -130,6 +130,20 @@ pub union IpAddress { pub v6: Ipv6Address, } +// Ensure ABI guarantees for IpAddress. +const _: () = { + #[repr(C, packed)] + struct PackedHelper(T); + + assert!(align_of::() == 4); + assert!(size_of::() == 16); + + // The type must be usable in a packed struct, even when it is normally + // 4 byte aligned. + assert!(align_of::>() == 1); + assert!(size_of::>() == 16); +}; + impl IpAddress { /// Zeroed variant where all bytes are guaranteed to be initialized to zero. pub const ZERO: Self = Self { addr: [0; 4] }; @@ -355,20 +369,6 @@ mod tests { assert_eq!(efi_mac_addr.into_ethernet_addr(), ethernet_octets); } - // Ensure that our IpAddress type can be put into a packed struct, - // even when it is normally 4 byte aligned. - #[test] - fn test_efi_ip_address_abi() { - #[repr(C, packed)] - struct PackedHelper(T); - - assert_eq!(align_of::(), 4); - assert_eq!(size_of::(), 16); - - assert_eq!(align_of::>(), 1); - assert_eq!(size_of::>(), 16); - } - /// Tests the From-impls from the documentation. #[test] fn test_promised_from_impls() { diff --git a/uefi-raw/src/protocol/device_path.rs b/uefi-raw/src/protocol/device_path.rs index fa05b616..a2f6f1e2 100644 --- a/uefi-raw/src/protocol/device_path.rs +++ b/uefi-raw/src/protocol/device_path.rs @@ -64,6 +64,13 @@ pub struct DevicePathProtocol { // followed by payload (dynamically sized) } +// Ensure ABI guarantees for DevicePathProtocol. The struct is naturally +// packed; thus, we don't need to explicitly specify `packed`. +const _: () = { + assert!(size_of::() == 4); + assert!(align_of::() == 1); +}; + impl DevicePathProtocol { pub const GUID: Guid = guid!("09576e91-6d3f-11d2-8e39-00a0c969723b"); @@ -300,16 +307,3 @@ pub struct DevicePathUtilitiesProtocol { impl DevicePathUtilitiesProtocol { pub const GUID: Guid = guid!("0379be4e-d706-437d-b037-edb82fb772a4"); } - -#[cfg(test)] -mod tests { - use super::*; - - /// Test that ensures the struct is packed. Thus, we don't need to - /// explicitly specify `packed`. - #[test] - fn abi() { - assert_eq!(size_of::(), 4); - assert_eq!(align_of::(), 1); - } -} From 7a76937a94092345000736ca132cd19f3b5ab7a3 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 23 Aug 2026 15:25:07 +0200 Subject: [PATCH 2/2] uefi: replace ABI-related unit tests with consts checks This is best practice in the ecosystem and ensures that the ABI is correct for the target platform - which may be different from the test platform. --- uefi/src/mem/util.rs | 25 +++++++++++-------------- uefi/src/proto/device_path/mod.rs | 7 ++++--- uefi/src/proto/pci/mod.rs | 5 ++++- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/uefi/src/mem/util.rs b/uefi/src/mem/util.rs index 34be52b2..fe27604f 100644 --- a/uefi/src/mem/util.rs +++ b/uefi/src/mem/util.rs @@ -110,6 +110,17 @@ mod tests { } } + // Some basic sanity checks of the helper types so that we can catch + // problems early that miri would detect otherwise. + const _: () = { + assert!(size_of::() == 4); + assert!(align_of::() == 1); + // The size is 16 instead of 4, as in Rust the size is always a + // multiple of the alignment. + assert!(size_of::() == 16); + assert!(align_of::() == 16); + }; + /// Function that behaves like the other UEFI functions. It takes a /// mutable reference to a buffer memory that represents a [`SomeData`] /// instance. @@ -139,20 +150,6 @@ mod tests { Ok(data) } - // Some basic sanity checks so that we can catch problems early that miri would detect - // otherwise. - #[test] - fn test_some_data_type_size_constraints() { - assert_eq!(size_of::(), 4); - assert_eq!(SomeData::alignment(), 1); - assert_eq!( - size_of::(), - 16, - "The size must be 16 instead of 4, as in Rust the runtime size is a multiple of the alignment." - ); - assert_eq!(SomeDataAlign16::alignment(), 16); - } - // Tests `uefi_function_stub_read` which is the foundation for the `test_make_boxed_utility` // test. #[test] diff --git a/uefi/src/proto/device_path/mod.rs b/uefi/src/proto/device_path/mod.rs index 8a615bed..0c8dfda1 100644 --- a/uefi/src/proto/device_path/mod.rs +++ b/uefi/src/proto/device_path/mod.rs @@ -509,6 +509,10 @@ pub struct DevicePath { data: [u8], } +// References to the DST are transmuted from/to slice references; ensure +// both have the same fat-pointer layout. +const _: () = assert!(size_of::<&DevicePath>() == size_of::<&[u8]>()); + impl ProtocolPointer for DevicePath { unsafe fn ptr_from_ffi(ptr: *const c_void) -> *const Self { // SAFETY: The memory is valid. @@ -1119,9 +1123,6 @@ mod tests { #[test] fn test_to_owned() { - // Relevant assertion to verify the transmute is fine. - assert_eq!(size_of::<&DevicePath>(), size_of::<&[u8]>()); - let raw_data = create_raw_device_path(); // SAFETY: The memory is valid. let dp = unsafe { DevicePath::from_ffi_ptr(raw_data.as_ptr().cast()) }; diff --git a/uefi/src/proto/pci/mod.rs b/uefi/src/proto/pci/mod.rs index cfd1539f..63677757 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -27,6 +27,10 @@ pub struct PciIoAddress { pub ext_reg: u32, } +// The address is converted to/from a raw u64 value; ensure both have the +// same size. +const _: () = assert!(size_of::() == size_of::()); + impl PciIoAddress { /// Create address pointing to the device identified by `bus`, `dev` and `fun` ids. #[must_use] @@ -170,7 +174,6 @@ mod tests { #[test] #[expect(clippy::unusual_byte_groupings)] fn test_pci_ioaddr_raw_conversion() { - assert_eq!(size_of::(), size_of::()); let srcaddr = PciIoAddress { reg: 0x11, fun: 0x33,