Re: [RFCv2 0/9] UEFI emulator for kexec

From: Philipp Rudo
Date: Fri Sep 06 2024 - 06:55:14 EST


Hi Ard,
Hi Jan,

On Wed, 28 Aug 2024 19:08:14 +0200
Ard Biesheuvel <ardb@xxxxxxxxxx> wrote:

[...]

> Thanks for putting this RFC together. This is useful work, and gives
> us food for thought and discussion.
>
> There are a few problems that become apparent when going through these changes.
>
> 1. Implementing UEFI entirely is intractable, and unnecessary.
> Implementing the subset of UEFI that is actually needed to boot Linux
> *is* tractable, though, but we need to work together to write this
> down somewhere.
> - the EFI stub needs the boot services for the EFI memory map and
> the allocation routines
> - GRUB needs block I/O
> - systemd-stub/UKI needs file I/O to look for sidecars
> - etc etc
>
> I implemented a Rust 'efiloader' crate a while ago that encapsulates
> most of this (it can boot Linux/arm64 on QEMU and boot x86 via GRUB in
> user space **). Adding file I/O to this should be straight-forward -
> as Lennart points out, we only need the protocol, it doesn't need to
> be backed by an actual file system, it just needs to be able to expose
> other files in the right way.
>
> 2. Running the UEFI emulator on bare metal is not going to scale.
> Cloning UART driver code and MMU code etc is a can of worms that you
> want to leave closed. And as Lennart points out, there is other
> hardware (TPM) that needs to be accessible as well. Providing a
> separate set of drivers for all hardware that the EFI emulator may
> need to access is not a tractable problem either.
>
> The fix for this, as I see it, is to run the EFI emulator in user
> space, to the point where the payload calls ExitBootServices(). This
> will allow all I/O and memory protocol to be implemented trivially,
> using C library routines. I have a crude prototype** of this running
> to the point where ExitBootServices() is called (and then it crashes).
> The tricky yet interesting bit here is how we migrate a chunk of user
> space memory to the bare metal context that will be created by the
> kexec syscall later (in which the call to ExitBootServices() would
> return and proceed with the boot). But the principle is rather
> straight-forward, and would permit us, e.g., to kexec an OS installer
> too.

I mostly agree on what you have wrote. But I see a big problem in
running the EFI emulator in user space when it comes to secure boot.
The chain of trust ends in the kernel. So it's the kernel that needs to
verify that the image to be loaded can be trusted. But when the EFI
runtime is in user space the kernel simply cannot do that. Which means,
if we want to go this way, we would need to extend the chain of trust
to user space. Which will be a whole bucket of worms, not just a can.

That's why I tend more to Jan's suggestion to include the EFI runtime
in the kernel. Alas, that comes with it's own problem, as that requires
to run code in the kernel that was never intended to run in kernel
context. So even when we can trust the code not to be malicious, we
cannot trust it to not accidentally change the system state in a way
the kernel doesn't expect...

Let me throw an other wild idea in the ring. Instead of implementing
a EFI runtime we could also include a eBPF version of the stub into the
images. kexec could then extract the eBPF program and let it run just
like any other eBPF program with all the pros (and cons) that come with
it. That won't be as generic as the EFI runtime, e.g. you couldn't
simply kexec any OS installer. On the other hand it would make it
easier to port UKIs et al. to non-EFI systems. What do you think?

Thanks
Philipp

> 3. We need to figure out how to support TPM and PCRs in the context of
> kexec. This is a fundamental issue with verified boot, given that the
> kexec PCR state is necessarily different from the boot state, and so
> we cannot reuse the TPM directly if we want to pretend that we are
> doing an ordinary boot in kexec. The alternative is to leave the TPM
> in a state where the kexec kernel can access its sealed secrets, and
> mock up the TCG2 EFI protocols using a shim that sits between the TPM
> hardware (as the real TCG2 protocols will be long gone) and the EFI
> payload. But as I said, this is a fundamental issue, as the ability to
> pretend that a kexec boot is a pristine boot would mean that verified
> boot is broken.
>
>
> As future work, I'd like to propose to collaborate on some alignment
> regarding a UEFI baseline for Linux, i.e., the parts that we actually
> need to boot Linux.
>
> For this series in particular, I don't see a way forward where we
> adopt this approach, and carry all this code inside the kernel.
>
> Thanks.
> Ard.
>