This article brings some background information for security advisories GHSA-6pp6-cm5h-86g5 and CVE-2025-2296.

Booting x86 linux kernels

Lets start with some tech background and history, which is helpful to understand the chain of events leading to CVE-2025-2296.

The x86 linux kernel has a 'setup' area at the start of the binary, and the traditional role for that area is to hold information needed by the linux kernel to boot, for example command line and initrd location. The boot loader patches the setup header before handing over control to the kernel, which allows the linux kernel to find the command line you have typed into the boot loader prompt. Booting in BIOS mode still works that way, and will most likely continue to do so until the BIOS mode days are numbered.

In the early days of UEFI support the boot process in UEFI mode worked quite simliar. It's known as 'EFI handover protocol'. It turned out to have a number of disadvantages though, for example passing additional information requires updating both linux kernel and the boot loader. The latter is true for BIOS mode too, but new development there are very rare with the world moving towards UEFI.

Enter 'EFI stub'. With this the linux kernel is simply an EFI binary. Execution starts in EFI mode, so the early kernel code can do EFI calls and gather all information needed to boot the kernel without depending on the boot loader to do this for the kernel. Version dependencies are gone. Additional bonus is that no kernel-specific knowledge is needed any more. Anything which is able to start efi binaries -- for example efi shell -- can be used to launch the kernel.

Direct kernel boot in qemu

Qemu offers the option to launch linux kernels directly, using the -kernel command line switch. What actually happens behind the scenes is that qemu exposes the linux kernel to the guest using the firmware config interface (fw_cfg for short). The virtual machine firmware (SeaBIOS or OVMF) will fetch the kernel from qemu, place it in guest memory and launch it.

OVMF supports both 'EFI stub' and 'EFI handover protocol' boot methods. It will try the modern 'EFI stub' way first, which actually is just 'try start as EFI binary'. Which btw. implies that you can load any EFI binary that way, this is not limited to linux kernels.

If starting the kernel as EFI binary fails OVMF will try to fallback to the old 'EFI handover protocol' method. OVMF names the latter 'legacy linux kernel loader' in messages printed to the screen.

Direct kernel boot and secure boot

So, what is the problem with secure boot? Well, there isn't only one problem, we had multiple issues:

  • qemu (prior to version 10.0) provides the linux kernel binary in two chunks, the setup area and the rest of the linux kernel. The setup area is patched by qemu to fill in some information which would otherwide be provided by the bootloader, as explained above. The patching breaks the secure boot signature.
  • So, if secure boot is enabled attempts to boot via 'EFI stub' will fail, the firmware rejects the binary due to the signature check failing. OVMF will fallback to the legacy 'EFI handover protocol' loader. The legacy loader does not do secure boot verification, which is the core of CVE-2025-2296. And this was essentially unfixable (in the firmware alone) because there simply is no valid secure boot signature due to the patching qemu is doing. Nevertheless there are some use cases which expect direct kernel boot with secure boot enabled to work. Catch 22.
  • Also note that the typical linux distro boot workflow is not that the linux kernel signature is verified by the firmware. Instead the first binary loaded is shim.efi (signed by microsoft and verified by the firmware). Shim in turn will verify the EFI binaries signed by the distribution (typically grub and the linux kernel).

Security impact

Secure boot bypass sounds scary, but is it really?

First, the bypass is restricted to exactly one binary, which is the linux kernel the firmware fetches from qemu. This issue does not allow to run arbitrary code inside the guest, for example some unsigned efi binary an attacker places on the ESP after breaking into the virtual machine.

Second, the guest has to trust the virtualization host to not do evil things. The host has full control over the environment the guest is running in. Providing the linux kernel image for direct kernel boot is only one little thing out of many. If an evil host wants attack/disturb the guest there are plenty of ways to do so. The host does not need some exploit for that.

Third, many typical virtual machine configurations do not use direct kernel boot. The kernel is loaded from the guest disk instead.

So, the actual impact is quite limited.

Fixing the whole mess

There is no quick and easy fix available. Luckily it is also not super urgent and critical. Time to play the long game ...

Fix #1: qemu exposes an unmodified kernel image to the guest now (additionally to the traditional setup/rest split which is kept for BIOS mode and backward compatibility). Fixes the first issue.

Fix #2: qemu can expose the shim binary to the guest, using the new -shim command line switch. Fixes the third issue. Both qemu changes are available in qemu version 10.0 (released April 2025) and newer.

Fix #3: OVMF companion changes for fixes #1 + #2, use the new fw_cfg items exposed by qemu. Available in edk2-stable202502 and newer. Both qemu and OVMF must be updated for this to work.

Fix #4: Add a config option to disable the legacy 'EFI handover protocol' loader. Leave it enabled by default because of the low security impact and the existing use cases, but print warnings to the console in case the legacy loader is used. Also present in edk2-stable202502 and newer. Addresses the second issue.

With all that in place it is possible to plug the CVE-2025-2296 hole, by flipping the new config switch to disabled.

Fix #5: Do not use the legacy loader in confidential VMs. Present in edk2-stable202511 and newer.

Roughly one year has been passed since the first changes have been committed to qemu and edk2. What happened? libvirt also got support for passing shim to the guest for direct kernel boot (version 11.2.0 and newer). The new versions have found their way into the major linux distributions. debian, ubuntu and fedora should all be ready for the next step now.

Fix #6: Flip the default value for the legacy loader config option to disabled. This update just landed in the edk2 git repository and will be in edk2-stable202602.

What is left to do? The final cleanup. Purge the legacy loader from the edk2 code base. Will probably happen a year or two down the road.

References

The edk2 changes are in X86QemuLoadImageLib.

The qemu changes are in hw/i386/x86-common.c.