testsAndMisc/docs/superpowers/evidence/boot-repair-bios-guard-2026-08-22.json
Krzysztof kuhy Rudnicki aec2b275c1
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
boot-repair: refuse shadow-file cleanup on BIOS systems with no ESP
clean_shadow_files() assumed UEFI with an ESP mounted at /boot. On a
BIOS/GRUB system with no ESP, /boot is a plain directory on the root
filesystem holding the only kernel the machine has -- and neither existing
guard catches it: findmnt /boot is empty (GUARD 1 passes) and there is no
EFI/ or loader/ (GUARD 2 passes). boot-repair therefore read the real
vmlinuz-linux and initramfs-linux.img as "orphaned kernel file(s)
shadowing the ESP" and deleted them, leaving the machine unbootable.

Measured 2026-08-22 in a vmbox BIOS/GRUB sandbox, not theorised: after
sudo boot-repair the guest never booted again.

GUARD 3: if /sys/firmware/efi does not exist the system booted via BIOS,
so there is no ESP and nothing in /boot can be a shadow. warn + return 1,
matching the existing guards' tone and exit convention. Overridable with
the existing --esp DEVICE -- naming the ESP by hand asserts the firmware
assumption the guard otherwise checks -- rather than adding a new flag.

The check goes through rpath(), so a --root fixture declares its own
firmware type and the case is testable without a VM. On the live UEFI
machine it resolves to the real /sys path, so behaviour there is unchanged.

Audit of the other BIOS-unsafe paths (none destructive, left alone):
  * mount_esp(): resolve_esp_device fails with no /boot fstab entry, so it
    reports and does not mount. Non-destructive.
  * sync_kernel_image()/rebuild_initramfs(): gated behind boot_is_mounted,
    which is false on BIOS, so they never write.
  * check_fstab_options(): reports "no /boot entry" -- noise on BIOS, but
    it writes nothing.
  * run_preflight(): aborts every kernel upgrade on a BIOS box. Fail-closed
    and non-destructive, so out of scope here.

Tests: three cases in tests/lib/boot_repair_bios_cases.sh (new file; the
existing case lib is at the 250-line cap) -- the guard refuses and explains,
--esp overrides it, and a UEFI fixture still cleans shadows as before.
Verified failing without the guard (38/4) and passing with it (42/0).

Sandbox: BIOS guest keeps both kernel files, says why, and still boots
afterwards (uname -r 7.1.9-arch1-2 after a clean poweroff). shellcheck clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdyMBGCrGVGDWtJ9xe48f3
2026-08-22 23:46:20 +02:00

68 lines
5.0 KiB
JSON

{
"intent": "Stop boot-repair from deleting the only kernel on a BIOS/GRUB system that has no EFI System Partition, without changing its behaviour on kuhy's UEFI machine.",
"scope": [
"linux_configuration/scripts/boot_recovery/boot-repair (clean_shadow_files, new system_is_uefi helper, header invariants, --esp help text)",
"linux_configuration/scripts/boot_recovery/tests/lib/boot_repair_bios_cases.sh (new)",
"linux_configuration/scripts/boot_recovery/tests/lib/boot_repair_cases.sh (make_root firmware parameter)",
"linux_configuration/scripts/boot_recovery/tests/test_boot_repair.sh (source + register the new cases)",
"Non-goal: fixing the other BIOS-unsafe-but-non-destructive paths (mount_esp, check_fstab_options, run_preflight) -- audited and left alone deliberately"
],
"changes": [
"Added GUARD 3 to clean_shadow_files(): when rpath(/sys/firmware/efi) is absent the system booted via BIOS, has no ESP, and /boot holds the real kernel -- warn and return 1 instead of deleting.",
"Added system_is_uefi(), expressed through rpath() so --root fixtures declare their own firmware type and the guard is testable without a VM; on ROOT=/ it resolves to the real /sys path.",
"Made the guard overridable via the existing --esp DEVICE rather than adding a new flag: naming the ESP by hand asserts the firmware assumption the guard checks.",
"Gave make_root() a third 'firmware' parameter defaulting to uefi (creates sys/firmware/efi), so the pre-existing UEFI-shaped fixtures keep their meaning.",
"Added three cases: BIOS guard refuses and explains, --esp overrides it, and a UEFI fixture still cleans shadows (regression guard for the no-behaviour-change requirement)."
],
"verification": [
{
"command": "linux_configuration/scripts/boot_recovery/tests/test_boot_repair.sh",
"result": "pass",
"evidence": "=== 42 passed, 0 failed === (was 33 before; +9 assertions across 3 new cases)"
},
{
"command": "test_boot_repair.sh with GUARD 3 neutered (system_is_uefi() { return 0; }) in a scratch copy",
"result": "fail-as-expected",
"evidence": "=== 38 passed, 4 failed === -- fails 'explains why it refused', 'names the override', 'does not claim shadows', 'real initramfs preserved'. Proves the new case actually catches the bug rather than passing vacuously."
},
{
"command": "vm new bg && vm run bg 'sudo /usr/local/sbin/boot-repair' (BIOS/GRUB sandbox)",
"result": "pass",
"evidence": "Guest confirmed BIOS-shaped first: no /sys/firmware/efi, no /boot fstab entry, findmnt /boot empty (GUARD 1 passes), no EFI//loader/ (GUARD 2 passes). boot-repair printed '[WARN] no /sys/firmware/efi -- this system booted via BIOS and has no ESP' and left /boot/vmlinuz-linux (17101312 b) and /boot/initramfs-linux.img (14957933 b) untouched. Exit 1 from the pre-existing 'no /boot entry in /etc/fstab' finding, which is BIOS noise, not a regression."
},
{
"command": "vm run bg 'sudo systemctl poweroff' then vm run bg 'uname -r'",
"result": "pass",
"evidence": "Host-side VERDICT: clean poweroff (reached 'reboot: Power down'), then the guest booted again into 7.1.9-arch1-2 with both kernel files present. This is the exact scenario that previously left the guest permanently unbootable."
},
{
"command": "vm run bg '~/tam/.../tests/test_boot_repair.sh'",
"result": "pass",
"evidence": "=== 42 passed, 0 failed === inside the BIOS guest as well as on the UEFI host."
},
{
"command": "./boot-repair --dry-run (on the real UEFI host)",
"result": "pass",
"evidence": "No BIOS warning; '[ OK ] /boot mounted (/dev/nvme0n1p1)', ESP kernel and initramfs both OK. Confirms the required no-behaviour-change on kuhy's actual machine."
},
{
"command": "shellcheck boot-repair tests/test_boot_repair.sh tests/lib/*.sh",
"result": "pass",
"evidence": "no findings"
},
{
"command": "pre-commit run --files <changed-files>",
"result": "pass",
"evidence": "all hooks pass, including the 250-line cap and shellcheck"
}
],
"risks": [
"A UEFI machine booted with efivars unavailable (e.g. an ISO rescue booted in legacy mode to repair a UEFI install) now refuses shadow cleanup. Mitigated by the --esp DEVICE override, which is named in the warning text itself.",
"The guard only covers the destructive path. A BIOS box still gets noisy FAIL lines from check_fstab_options and mount_esp, and run_preflight would abort kernel upgrades there. All are fail-closed and non-destructive; noted in the commit message rather than fixed, to keep this change narrow."
],
"rollback": [
"git revert the commit; boot-repair is self-contained and installed by copy, so re-run linux_configuration/scripts/boot_recovery/install.sh to push the reverted script to /usr/local/sbin.",
"After rollback, confirm ./boot-repair --dry-run on the UEFI host still reports '/boot mounted' and do NOT run it on any BIOS system -- the deletion bug is live again."
]
}