Skip to content

Commit d23e994

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 get multiple advantages from that: - no more chance to have uninitialized bytes - We can `Debug`-print the type - Some implementations become simpler
1 parent e03ebda commit d23e994

File tree

2 files changed

+46
-72
lines changed

2 files changed

+46
-72
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

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

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

363349
let core_addr = StdIpAddr::V6(StdIpv6Addr::from(TEST_IPV6));
364350
let uefi_addr = IpAddress::from(core_addr);
365-
assert_eq!(unsafe { uefi_addr.v6.0 }, TEST_IPV6);
351+
assert_eq!(unsafe { uefi_addr.as_ipv6() }.octets(), TEST_IPV6);
366352
}
367353

368354
/// Tests the From-impls as described in the module description.
@@ -412,30 +398,18 @@ mod tests {
412398
}
413399
}
414400

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

uefi/src/proto/network/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ impl IpAddress {
4444
#[must_use]
4545
const unsafe fn from_raw(ip_addr: uefi_raw::net::IpAddress, is_ipv6: bool) -> Self {
4646
if is_ipv6 {
47-
Self::new_v6(unsafe { ip_addr.v6.0 })
47+
Self::new_v6(ip_addr.0)
4848
} else {
49-
Self::new_v4(unsafe { ip_addr.v4.0 })
49+
Self::new_v4([ip_addr.0[0], ip_addr.0[1], ip_addr.0[2], ip_addr.0[3]])
5050
}
5151
}
5252

0 commit comments

Comments
 (0)