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

gsoc2026-bootcounter on GitHub

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:

  1. Loader: BS->GetNextMonotonicCount() called once before

  2. Metadata injection: file_addmetadata(MODINFOMD_EFI_MTC)

    • in bi_load_efi_data(), following the MODINFOMD_EFI_FB pattern.
  3. Architecture tags: MODINFOMD_EFI_MTC defined per-arch —

    • 0x1009 (x86), 0x1004 (arm64), 0x1005 (riscv) — no collisions.
  4. 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.

  5. sysctl interface: kern.bootcount (uint64_t, write-once-then-lock)

    • and kern.bootcount_fw_backed (bool). Post-lock writes return EPERM.
  6. 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

Test Plan

CategoryGsoc

SummerOfCode2026Projects/EFIBackedPersistentBootCounterForEarlyBootHMACInFreeBSD (last edited 2026-07-09T02:26:08+0000 by VidithHundekar)