UefiPayloadPkg/BaseSerialPortLibHob: Close EBS event in destructor

DxeBaseSerialPortLibHobConstructor() creates an
EVT_SIGNAL_EXIT_BOOT_SERVICES event so that SerialPortWrite() can stop
touching the UART once the OS owns the hardware. The library comes in
through the DebugLib -> SerialPortLib chain of every DXE driver, so
every driver registers such an event during
ProcessLibraryConstructorList().

Drivers whose entry point returns an error leave that event behind:
_ModuleEntryPoint() runs ProcessLibraryDestructorList() and the DXE
core unloads the image, but this library declares no destructor, so the
ExitBootServices event stays registered with a NotifyFunction pointing
into the freed image. When the OS loader eventually calls
ExitBootServices, the DXE core dispatches the stale event and branches
into whatever now occupies that address.

On the AArch64 chainloaded payload this crashes: GraphicsOutputDxe
fails its entry point (no framebuffer HOB), leaks the event, and
DiskIoDxe is loaded into the freed pages at a slightly higher base. The
stale NotifyFunction then targets DiskIoDxe's PE header page, which
image protection has marked non-executable, and the resulting
instruction abort takes the system down right after "EFI stub: Exiting
boot services...".

Store the event handle in a module-scope variable and add a matching
destructor that closes it. That way an unloaded driver no longer leaves
a dangling ExitBootServices callback behind.

Cc: Benjamin Doron <benjamin.doron@9elements.com>
Cc: Gua Guo <gua.guo@intel.com>
Cc: Guo Dong <guo.dong@intel.com>
Cc: James Lu <james.lu@intel.com>
Cc: Sean Rhodes <sean@starlabs.systems>
Cc: Shuo Liu <shuo.liu@intel.com>
Assisted-by: claude-opus-5
Signed-off-by: Alexander Graf <graf@amazon.com>
This commit is contained in:
Alexander Graf 2026-07-30 21:57:46 +00:00
parent 3a17aa8196
commit 339e8221af
2 changed files with 39 additions and 3 deletions

View file

@ -9,6 +9,8 @@
extern BOOLEAN mBaseSerialPortLibHobAtRuntime;
STATIC EFI_EVENT mBaseSerialPortLibHobExitBootServicesEvent;
/**
Set mSerialIoUartLibAtRuntime flag as TRUE after ExitBootServices.
@ -44,13 +46,46 @@ DxeBaseSerialPortLibHobConstructor (
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_EVENT SerialPortLibHobExitBootServicesEvent;
return SystemTable->BootServices->CreateEvent (
EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_NOTIFY,
BaseSerialPortLibHobExitBootServicesEvent,
NULL,
&SerialPortLibHobExitBootServicesEvent
&mBaseSerialPortLibHobExitBootServicesEvent
);
}
/**
The destructor closes the ExitBootServices event.
A driver that fails its entry point is unloaded again by the DXE core, but
this library has already registered its ExitBootServices callback by then.
Close the event in the destructor so that a stale notification function
pointing into the unloaded image is not left behind.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS No event was registered, or it was closed.
@retval other CloseEvent () failed.
**/
EFI_STATUS
EFIAPI
DxeBaseSerialPortLibHobDestructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
if (mBaseSerialPortLibHobExitBootServicesEvent == NULL) {
return EFI_SUCCESS;
}
Status = SystemTable->BootServices->CloseEvent (mBaseSerialPortLibHobExitBootServicesEvent);
if (!EFI_ERROR (Status)) {
mBaseSerialPortLibHobExitBootServicesEvent = NULL;
}
return Status;
}

View file

@ -14,6 +14,7 @@
VERSION_STRING = 1.0
LIBRARY_CLASS = SerialPortLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
CONSTRUCTOR = DxeBaseSerialPortLibHobConstructor
DESTRUCTOR = DxeBaseSerialPortLibHobDestructor
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec