EFI-Backed Persistent Boot Counter for Early-Boot HMAC in FreeBSD
Project Information
Student |
Vidith Hundekar (VidithHundekar) |
Mentor |
Bruce Simpson (bms@FreeBSD.org) |
Organization |
FreeBSD Foundation |
Duration |
350 hours (Large Project) |
Period |
25 May 2026 – 24 August 2026 |
Repository |
Summary
FreeBSD has no persistent boot counter. Early-boot networking subsystems — WireGuard, SCTP — compute HMAC tags before the entropy pool is seeded, producing identical tags across reboots and exposing the system to replay attacks. The existing mitigation, /boot/entropy, fails on VM clones, fresh installs, and disk compromise.
This project captures the UEFI firmware monotonic counter during the loader's Boot Services phase and passes it to the kernel via the existing preload metadata transport. The counter is exposed as kern.bootcount (uint64_t) from SI_SUB_KMEM onward — before any networking subsystem initializes.
Approach
The implementation follows the existing FreeBSD metadata transport pattern, identical to how the EFI memory map and framebuffer are passed from loader to kernel:
Loader: BS->GetNextMonotonicCount() called once before
ExitBootServices(). Full 64-bit result stored in efi_boot_mtc.
Metadata injection: file_addmetadata(MODINFOMD_EFI_MTC)
- in bi_load_efi_data(), following the MODINFOMD_EFI_FB pattern.
Architecture tags: MODINFOMD_EFI_MTC defined per-arch —
- 0x1009 (x86), 0x1004 (arm64), 0x1005 (riscv) — no collisions.
Kernel ingestion: Two-stage SYSINIT in kern_bootcount.c.
- Stage 1 (SI_SUB_KMEM): reads BS value via MD_FETCH, locks immediately if non-zero. Stage 2 (SI_SUB_DRIVERS, SI_ORDER_THIRD): runs only if Stage 1 found
no BS value. Calls RS->GetNextHighMonotonicCount() once as a UEFI fallback when BS capture returned zero. Uses (rs_high << 32) as boot epoch with low 32 bits set to zero.
- Stage 1 (SI_SUB_KMEM): reads BS value via MD_FETCH, locks immediately if non-zero. Stage 2 (SI_SUB_DRIVERS, SI_ORDER_THIRD): runs only if Stage 1 found
sysctl interface: kern.bootcount (uint64_t, write-once-then-lock)
- and kern.bootcount_fw_backed (bool). Post-lock writes return EPERM.
Non-UEFI fallback: rc script for systems with no EFI firmware
- at all (BIOS, U-Boot). Reads/increments /var/db/bootcount, writes to kern.bootcount once, activates lock. (post-midterm)
Deliverables
Pre-Midterm (done)
Phase 1 — Loader counter capture (done)
capture_monotonic_counter() in stand/efi/loader/efi_main.c uses BS->GetNextMonotonicCount() for the full 64-bit value. Verified across 6 reboots in QEMU+OVMF — strictly increasing, never repeats.
kern_bootcount: BS value = 0x000000310000000b kern_bootcount: BS value = 0x000000330000000b kern_bootcount: BS value = 0x000000350000000b
Exit criterion met: counter non-zero, strictly increasing, captured before ExitBootServices().
Commits: 2572c36, f94c331 2721f9f,
Phase 2 — Metadata transport (done)
MODINFOMD_EFI_MTC defined in all three architecture metadata headers. file_addmetadata() call added in bootinfo.c. Case statements added to subr_module.c for type and value printing.
Exit criterion met: tag values collision-free, compiles cleanly.
Commit: 3120cea
Phase 3 — Kernel ingestion and sysctl (done)
sys/kern/kern_bootcount.c implements two-stage SYSINIT. kern.bootcount and kern.bootcount_fw_backed exposed via sysctl. GetNextHighMonotonicCount wrapper added to efirt.c. Public KPI in sys/sys/bootcount.h. Wired into sys/conf/files. ATF regression tests written and wired into tests/sys/kern/Makefile.
QEMU end-to-end verification:
kern_bootcount: BS value = 0x0000006e0000000a kern.bootcount: 472446402570 kern.bootcount_fw_backed: 1 sysctl -w kern.bootcount=0 → Operation not permitted
Exit criterion met: sysctl returns BS value, write returns EPERM, fw_backed = 1, stable across repeated reads.
Commits: 591fcda, 3f8bf21, f30ba2d, 825487f, 284d706
Post-Midterm
- Non-UEFI fallback rc script — /var/db/bootcount, tested on QEMU+SeaBIOS
- Cross-architecture testing — aarch64, riscv64
- bsnmpd integration — snmpEngineBoots OID
- Man pages — bootcount(4) and kern.bootcount(9)
- Final PR on reviews.freebsd.org
Test Plan
QEMU+OVMF end-to-end: loader prints counter, kernel reads same
- value at SYSINIT, sysctl matches. Verified across 6 reboots.
Write-lock: sysctl -w kern.bootcount=0 returns EPERM.
ATF tests: bootcount_readable, bootcount_stable,
- bootcount_fw_backed, bootcount_eperm — all passing.
Non-UEFI fallback: QEMU+SeaBIOS — rc script sets counter,
- lock activates. (post-midterm)
Cross-arch: aarch64 and riscv64. (post-midterm)