Skip to content

A kernel copied onto the ESP takes its name only once it reads back as its source - #261

Merged
DevomB merged 4 commits into
mainfrom
kernel-copy-compared
Oct 9, 2026
Merged

DevomB merged 4 commits into
mainfrom
kernel-copy-compared

Conversation

@DevomB

@DevomB DevomB commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Built on #258 (commit-record-fails, in the integrator's batch). Until it lands, the diff against main includes its two commits. This PR's own changes are its top two commits, 5688e3d and d0e6f32.

What was wrong (from an adversarial re-read of the update chain on main). Of the four places Kryptik copies a kernel onto an ESP, two looked at what landed:

  • the installer compares BOOTX64.EFI with the slot A kernel (kryptik-install.sh:313),
  • kryptik-update apply hashes its staged kernel against the signed manifest (kryptik-update:369).

The other two did not:

  • boot-success's commit copied the trial slot's kernel to BOOTX64.EFI.new, fsynced it and renamed it,
  • kryptik-recover did the same for the boot file in --commit-slot, and for the medium's kernel in --restore-slot.

A copy that read back wrong (a failing card, a full or damaged FAT) became a boot file that Secure Boot refuses. After a commit, only the slot's own firmware entry then stood between the machine and an unbootable disk.

What changed.

  • build/service-scripts/boot-success.sh: the copy is compared with its source (cmp -s) between the fsync and the rename. A copy that differs fails the commit as a failed write already does: the trial stays, commit-failed is recorded and the boot file is unchanged.
  • The compare reads what the ESP holds. The copy is dropped from the page cache after its fsync (dd iflag=nocache count=0, best effort), which a non-author read pointed out: without that, the compare read the copy still in memory. kryptik-update's hash of its staged kernel does the same.
  • tools/update/kryptik-recover: both copies are compared before their rename. A mismatch stops the tool, with BOOTX64.EFI unchanged, or with nothing committed in the restore.
  • docs/design/boot-and-updates.md: step 5 says ".new, fsync, compare, rename" (that paragraph is rewrapped).
  • tools/tests/boot-success.sh: a cmp stand-in passes through to the real one, except that it can make the copy to BOOTX64.EFI.new read back wrong. The new case expects commit-failed b, the old boot file, the record still naming a, and the trial kept.

How the run proves it. CI's fixture step runs the new case. Distro run 37883969899 commits through boot-success in the update and install suites, and the integrity suite runs kryptik-recover --commit-slot and --restore-slot from the medium.

DevomB added 3 commits October 8, 2026 19:55
…mmit: the trial stays, and the next boot commits again

boot-success's commit_slot returned 0 once BOOTX64.EFI was the slot's kernel,
whatever became of the committed-slot record. With the record unwritten, the
caller cleared the trial, recorded "commit b", forgot the entries and raised
the clock's floor while the ESP still named a: the next boot found b running
uncommitted and rebooted once for nothing, and kryptik-update refused every
apply from b until kryptik-recover --commit-slot b from the medium. Now the
record is part of the commit. Failing it keeps the trial, and since BOOTX64.EFI
already boots b and kryptik-efiboot's entries come after it in BootOrder, the
next boot finds BOOTX64.EFI already b and writes only the record.
kryptik-recover's put already dies the same way.

kryptik-update says which ESP write failed when the staged kernel's flush or
the version record fails, instead of leaving set -e to exit without a word.
…slot: a boot file that could not be replaced left the ESP naming a slot the firmware does not boot

From a non-author read: when the copy, flush or rename of BOOTX64.EFI failed,
commit_slot still rewrote committed-slot to the trial's slot. The next boot
took BOOTX64.EFI, the old slot, whose applies kryptik-update then refused as
not committed. The fixture fails that step and expects the record unchanged.
…s its source: boot-success's commit and kryptik-recover's commit and restore now compare, as the installer and kryptik-update already did

boot-success copied the trial slot's kernel to BOOTX64.EFI.new, fsynced and
renamed it without looking at what landed, and kryptik-recover did the same for
the boot file and for the medium's kernel. A copy that read back wrong became
a boot file Secure Boot refuses. Now the copy is compared with its source
before the rename: a failed compare fails the commit and the trial stays, as a
failed write already does, and recover stops before anything is committed. The
installer compares BOOTX64.EFI and kryptik-update hashes its staged kernel.
The boot-success fixture makes the copy read back wrong and expects the old
boot file and record and the trial kept.
…ped from the page cache after its fsync, so the compare reads the ESP and not the copy still in memory

From a non-author read: cmp right after sync -f read the cached pages, which
says little more than cp's own status. dd iflag=nocache count=0 drops a file's
clean pages, best effort; boot-success's and kryptik-recover's compares and
kryptik-update's hash of its staged kernel now read what the ESP holds.
@DevomB
DevomB merged commit aef8056 into main Oct 9, 2026
21 checks passed
@DevomB
DevomB deleted the kernel-copy-compared branch October 9, 2026 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant