UEFI Memory Ownership
Overview
A recurring source of confusion during development was determining who owns a memory buffer returned by a UEFI API.
Some functions expect the caller to allocate the buffer, while others allocate memory internally and expect the caller to free it afterwards.
The function signature alone is often insufficient to determine the ownership model.
Initial Assumption
A common assumption is:
If a function receives a pointer-to-pointer, the caller probably allocates the memory.
This turned out to be incorrect for several UEFI interfaces.
Caller-Allocated Example: GetMemoryMap
GetMemoryMap() requires the caller to provide the buffer.
Simplified usage:
Status = GetMemoryMap(
&Size,
Buffer,
&MapKey,
&DescriptorSize,
&Version
);
The workflow is:
- Query required size
- Allocate buffer with
AllocatePool() - Call
GetMemoryMap()again - Free the buffer when finished
Ownership:
- Allocation: caller
- Freeing: caller
Callee-Allocated Example: QueryMode
EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode() behaves differently.
Signature:
EFI_STATUS
(EFIAPI *QueryMode)(
EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
UINT32 ModeNumber,
UINTN *SizeOfInfo,
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
);
At first glance, the ownership is not obvious.
The UEFI specification clarifies that:
Infois a callee-allocated buffer.
This means:
- The graphics driver allocates the memory internally.
- The caller receives the pointer.
- The caller must release it using
FreePool().
Correct usage:
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info = NULL;
Status = gop->QueryMode(gop, Mode, &InfoSize, &Info);
if (!EFI_ERROR(Status)) {
/* use Info */
FreePool(Info);
}
Why the Signature Is Misleading
Both APIs involve pointers, but they follow different ownership contracts.
| API | Allocation | Freeing |
|---|---|---|
GetMemoryMap() | Caller | Caller |
QueryMode() | Callee | Caller |
Without consulting the specification, both could easily be implemented incorrectly.
Practical Rule
When implementing a new UEFI API call:
- Read the function signature.
- Read the UEFI specification section for that function.
- Determine whether the buffer is:
- caller-allocated
- callee-allocated
- static / firmware-owned
Never infer ownership from the C prototype alone.
Why This Matters
Incorrect ownership handling can cause:
- Memory leaks
- Double frees
- Invalid
FreePool()calls - Corrupted firmware state
- Failures during boot services operations
These bugs are often harder to diagnose than ordinary user-space memory errors because they occur inside the firmware execution environment.
Key Takeaways
- UEFI memory ownership is function-specific.
- Pointer-to-pointer parameters do not automatically imply caller allocation.
GetMemoryMap()is caller-allocated.QueryMode()is callee-allocated.- The specification must be consulted to determine the correct ownership model.
This finding significantly changed the approach used when implementing new UEFI protocol interactions in the project.