Skip to content

Commit 992f8ae

Browse files
committed
uefi-raw: net: covert union to normal type
The union makes some things hard to work with, and we can simply remove it. We gain multiple advantages from that: - no more chance to have uninitialized bytes - We can `Debug`-print the type - Some implementations become simpler
1 parent ab4e890 commit 992f8ae

File tree

1 file changed

+44
-70
lines changed

1 file changed

+44
-70
lines changed

uefi-raw/src/net.rs

Lines changed: 44 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@
2828
//! - `[u8; 6]` -> [`MacAddress`]
2929
//! - `[u8; 32]` -> [`MacAddress`]
3030
31-
use core::fmt::{Debug, Formatter};
3231
use core::net::{IpAddr as StdIpAddr, Ipv4Addr as StdIpv4Addr, Ipv6Addr as StdIpv6Addr};
33-
use core::{fmt, mem};
3432

3533
/// An IPv4 internet protocol address.
3634
///
@@ -92,38 +90,27 @@ impl From<Ipv6Address> for StdIpv6Addr {
9290
}
9391
}
9492

95-
/// EFI ABI-compatible union of an IPv4 or IPv6 internet protocol address.
96-
///
97-
/// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. This
98-
/// type is defined in the same way as edk2 for compatibility with C code. Note
99-
/// that this is an **untagged union**, so there's no way to tell which type of
100-
/// address an `IpAddress` value contains without additional context.
93+
/// EFI ABI-compatible union of an IPv4 or IPv6 internet protocol address,
94+
/// corresponding to `EFI_IP_ADDRESS` type in the UEFI specification.
10195
///
10296
/// See the [module documentation] to get an overview over the relation to the
10397
/// types from [`core::net`].
10498
///
99+
/// # UEFI Information
100+
/// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. Instead
101+
/// of using an untagged C union, we use this more rusty type. ABI-wise it is
102+
/// the same but less cumbersome to work with in Rust.
103+
///
105104
/// [module documentation]: self
106-
#[derive(Clone, Copy)]
107-
#[repr(C)]
108-
pub union IpAddress {
109-
/// An IPv4 internet protocol address.
110-
pub v4: Ipv4Address,
111-
112-
/// An IPv6 internet protocol address.
113-
pub v6: Ipv6Address,
114-
115-
/// This member serves to align the whole type to 4 bytes as required by
116-
/// the spec. Note that this is slightly different from `repr(align(4))`,
117-
/// which would prevent placing this type in a packed structure.
118-
align_helper: [u32; 4],
119-
}
105+
#[derive(Debug, Clone, Copy)]
106+
#[repr(C, align(4))]
107+
pub struct IpAddress(pub [u8; 16]);
120108

121109
impl IpAddress {
122110
/// Construct a new zeroed address.
123111
#[must_use]
124112
pub const fn new_zeroed() -> Self {
125-
// SAFETY: All bit patterns are valid.
126-
unsafe { mem::zeroed() }
113+
Self([0; 16])
127114
}
128115

129116
/// Construct a new IPv4 address.
@@ -132,10 +119,9 @@ impl IpAddress {
132119
/// is needed.
133120
#[must_use]
134121
pub const fn new_v4(octets: [u8; 4]) -> Self {
135-
// Initialize all bytes to zero first.
136-
let mut obj = Self::new_zeroed();
137-
obj.v4 = Ipv4Address(octets);
138-
obj
122+
Self([
123+
octets[0], octets[1], octets[2], octets[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124+
])
139125
}
140126

141127
/// Construct a new IPv6 address.
@@ -144,17 +130,14 @@ impl IpAddress {
144130
/// is needed.
145131
#[must_use]
146132
pub const fn new_v6(octets: [u8; 16]) -> Self {
147-
// Initialize all bytes to zero first.
148-
let mut obj = Self::new_zeroed();
149-
obj.v6 = Ipv6Address(octets);
150-
obj
133+
Self(octets)
151134
}
152135

153136
/// Returns the octets of the union. Without additional context, it is not
154137
/// clear whether the octets represent an IPv4 or IPv6 address.
155138
#[must_use]
156139
pub const fn octets(&self) -> [u8; 16] {
157-
unsafe { self.v6.octets() }
140+
self.0
158141
}
159142

160143
/// Returns a raw pointer to the IP address.
@@ -181,11 +164,12 @@ impl IpAddress {
181164
#[must_use]
182165
pub fn to_std_ip_addr(self, is_ipv6: bool) -> StdIpAddr {
183166
if is_ipv6 {
184-
StdIpAddr::V6(StdIpv6Addr::from(unsafe { self.v6.octets() }))
167+
StdIpAddr::V6(StdIpv6Addr::from(self.octets()))
185168
} else {
186169
let has_extra_bytes = self.octets()[4..].iter().any(|&x| x != 0);
187170
assert!(!has_extra_bytes);
188-
StdIpAddr::V4(StdIpv4Addr::from(unsafe { self.v4.octets() }))
171+
let octets: [u8; 4] = self.octets()[..4].try_into().unwrap();
172+
StdIpAddr::V4(StdIpv4Addr::from(octets))
189173
}
190174
}
191175

@@ -212,20 +196,7 @@ impl IpAddress {
212196
/// additional context that the IP is indeed an IPv6 address.
213197
#[must_use]
214198
pub unsafe fn as_ipv6(&self) -> Ipv6Address {
215-
Ipv6Address::from(unsafe { self.v6.octets() })
216-
}
217-
}
218-
219-
impl Debug for IpAddress {
220-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
221-
f.debug_struct("IpAddress")
222-
// SAFETY: All constructors ensure that all bytes are always
223-
// initialized.
224-
.field("v4", &unsafe { self.v4 })
225-
// SAFETY: All constructors ensure that all bytes are always
226-
// initialized.
227-
.field("v6", &unsafe { self.v6 })
228-
.finish()
199+
Ipv6Address::from(self.octets())
229200
}
230201
}
231202

@@ -336,6 +307,18 @@ mod tests {
336307
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
337308
];
338309

310+
/// Ensures ABI-compatibility, as described here:
311+
/// <https://github.com/tianocore/edk2/blob/b1887152024c7eb0cc7de735b0b57febd6099bf9/MdePkg/Include/Uefi/UefiBaseType.h#L100>
312+
#[test]
313+
fn test_abi() {
314+
assert_eq!(size_of::<IpAddress>(), 16);
315+
assert_eq!(align_of::<IpAddress>(), 4);
316+
assert_eq!(size_of::<Ipv4Address>(), 4);
317+
assert_eq!(align_of::<Ipv6Address>(), 1);
318+
assert_eq!(size_of::<Ipv6Address>(), 16);
319+
assert_eq!(align_of::<Ipv6Address>(), 1);
320+
}
321+
339322
/// Test round-trip conversion between `Ipv4Address` and `StdIpv4Addr`.
340323
#[test]
341324
fn test_ip_addr4_conversion() {
@@ -359,11 +342,14 @@ mod tests {
359342
fn test_ip_addr_conversion() {
360343
let core_addr = StdIpAddr::V4(StdIpv4Addr::from(TEST_IPV4));
361344
let uefi_addr = IpAddress::from(core_addr);
362-
assert_eq!(unsafe { uefi_addr.v4.0 }, TEST_IPV4);
345+
assert_eq!(
346+
unsafe { uefi_addr.try_as_ipv4().unwrap() }.octets(),
347+
TEST_IPV4
348+
);
363349

364350
let core_addr = StdIpAddr::V6(StdIpv6Addr::from(TEST_IPV6));
365351
let uefi_addr = IpAddress::from(core_addr);
366-
assert_eq!(unsafe { uefi_addr.v6.0 }, TEST_IPV6);
352+
assert_eq!(unsafe { uefi_addr.as_ipv6() }.octets(), TEST_IPV6);
367353
}
368354

369355
/// Tests the From-impls as described in the module description.
@@ -413,30 +399,18 @@ mod tests {
413399
}
414400
}
415401

416-
/// Tests that all bytes are initialized and that the Debug print doesn't
417-
/// produce errors, when Miri executes this.
418-
#[test]
419-
fn test_ip_address_debug_memory_safe() {
420-
let uefi_addr = IpAddress::new_v6(TEST_IPV6);
421-
std::eprintln!("{uefi_addr:#?}");
422-
}
423-
424402
/// Tests the expected flow of types in a higher-level UEFI API.
425403
#[test]
426404
fn test_uefi_flow() {
427405
fn efi_retrieve_efi_ip_addr(addr: &mut IpAddress, is_ipv6: bool) {
428-
// SAFETY: Alignment is guaranteed and memory is initialized.
429-
unsafe {
430-
addr.v4.0[0] = 42;
431-
addr.v4.0[1] = 42;
432-
addr.v4.0[2] = 42;
433-
addr.v4.0[3] = 42;
434-
}
406+
addr.0[0] = 42;
407+
addr.0[1] = 42;
408+
addr.0[2] = 42;
409+
addr.0[3] = 42;
410+
435411
if is_ipv6 {
436-
unsafe {
437-
addr.v6.0[14] = 42;
438-
addr.v6.0[15] = 42;
439-
}
412+
addr.0[14] = 42;
413+
addr.0[15] = 42;
440414
}
441415
}
442416

0 commit comments

Comments
 (0)