Skip to content

Commit f4673cf

Browse files
authored
Merge pull request #1558 from nicholasbishop/bishop-handle-null-page-alloc
uefi: Improve handling of null-address allocations in allocate_pages
2 parents 07938b6 + d42a3bf commit f4673cf

File tree

2 files changed

+33
-5
lines changed

2 files changed

+33
-5
lines changed

uefi/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
- `SimpleNetwork::transmit` now passes the correct buffer size argument.
1414
Previously it incorrectly added the header size to the buffer length, which
1515
could cause the firmware to read past the end of the buffer.
16+
- `boot::allocate_pages` no longer panics if the allocation is at address
17+
zero. The allocation is retried instead, and in all failure cases an error is
18+
returned rather than panicking.
1619

1720

1821
# uefi - 0.34.1 (2025-02-07)

uefi/src/boot.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,40 @@ pub fn allocate_pages(ty: AllocateType, mem_ty: MemoryType, count: usize) -> Res
134134
let bt = boot_services_raw_panicking();
135135
let bt = unsafe { bt.as_ref() };
136136

137-
let (ty, mut addr) = match ty {
137+
let (ty, initial_addr) = match ty {
138138
AllocateType::AnyPages => (0, 0),
139139
AllocateType::MaxAddress(addr) => (1, addr),
140140
AllocateType::Address(addr) => (2, addr),
141141
};
142-
let addr =
143-
unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr) }.to_result_with_val(|| addr)?;
144-
let ptr = addr as *mut u8;
145-
Ok(NonNull::new(ptr).expect("allocate_pages must not return a null pointer if successful"))
142+
143+
let mut addr1 = initial_addr;
144+
unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr1) }.to_result()?;
145+
146+
// The UEFI spec allows `allocate_pages` to return a valid allocation at
147+
// address zero. Rust does not allow writes through a null pointer (which
148+
// Rust defines as address zero), so this is not very useful. Only return
149+
// the allocation if the address is non-null.
150+
if let Some(ptr) = NonNull::new(addr1 as *mut u8) {
151+
return Ok(ptr);
152+
}
153+
154+
// Attempt a second allocation. The first allocation (at address zero) has
155+
// not yet been freed, so if this allocation succeeds it should be at a
156+
// non-zero address.
157+
let mut addr2 = initial_addr;
158+
let r = unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr2) }.to_result();
159+
160+
// Free the original allocation (ignoring errors).
161+
let _unused = unsafe { (bt.free_pages)(addr1, count) };
162+
163+
// Return an error if the second allocation failed, or if it is still at
164+
// address zero. Otherwise, return a pointer to the second allocation.
165+
r?;
166+
if let Some(ptr) = NonNull::new(addr2 as *mut u8) {
167+
Ok(ptr)
168+
} else {
169+
Err(Status::OUT_OF_RESOURCES.into())
170+
}
146171
}
147172

148173
/// Frees memory pages allocated by [`allocate_pages`].

0 commit comments

Comments
 (0)