initrd/bin/kexec-sign-config.sh: stop re-hashing stale default entry paths on -u update#2150
Conversation
There was a problem hiding this comment.
Copilot wasn't able to review any files in this pull request.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
QubesOS automated testing was testing (results still available as writing):
So the bug is new. Note that replacing xen-4.19.4.gz with xen-4.19.5.gz landed July 2nd, as per normal testing->release falling through all current automated testing in place for QubesOS QA/Dasharo. CC: @marmarek @macpijan @pietrushnic, let me know how we want to handle this forward.
|
7acbb08 to
3950b73
Compare
…paths on -u update Remove the -u block that re-hashed file paths extracted from the old kexec_default_hashes.txt. Those paths are from a previously saved default boot entry. When a distro replaces a versioned boot file in-place (e.g. xen-4.19.4.gz to xen-4.19.5.gz), the old path no longer exists on disk and sha256sum fails. The re-hash was redundant: the find ./ -type f ! -path "./kexec*" -print0 | xargs -0 sha256sum command in the same -u block already generates a complete, fresh hash of all current /boot files. The re-hash also behaved unpredictably across bash builds. Before commit 1f6a975 (2025-02-07), the subshell had no TRACE_FUNC and set -e was not enforced on the pipeline failure -- the sha256sum error was silently masked by print_tree returning 0, the subshell exited 0, and a partial hash file was signed. After 1f6a975 added TRACE_FUNC as the first command in the subshell, set -e became enforced, making the same pipeline failure fatally abort the subshell before print_tree could mask it. In both cases the re-hash produced the wrong result; the fix is to remove it. The user re-saves the default boot entry from the boot menu to regenerate kexec_default_hashes.txt with current paths. Regression lineage: - 2017 - file created without re-hash - Mar 14, 2018 - re-hash added in update_checksums() inside gui-init (Kyle Rankin, commit 769f6a7) - Jul 5, 2019 - update_checksums() moved to /etc/functions (Matt DeVillier, commit ed2f19d) - Oct 8, 2020 - re-hash moved into kexec-sign-config under -u flag; -u and -r passed together in a single call (alex-nitrokey, commit 8bfae52) - Feb 7, 2025 - TRACE_FUNC placed as first command inside the subshell -- this is where a stale-path sha256sum failure changes from silently masked to fatally aborting (Thierry Laurion, commit 1f6a975) Mitigation on unpatched firmware (recovery shell): mount -o remount,rw /boot rm /boot/kexec_default_hashes.txt mount -o remount,ro /boot Then re-run Update checksums. With the file gone, the -e guard skips the re-hash entirely. Then set a new default from the boot menu to regenerate kexec_default_hashes.txt with current paths. Signed-off-by: Thierry Laurion <insurgo@riseup.net>
a3e7ab0 to
d16641e
Compare
Inside bash (... ) subshells, set -e does not reliably abort on
pipeline failures -- enforcement depends on the type of the first
command in the subshell (builtin vs function call). A later command
like print_tree that always returns 0 masks the pipeline failure
exit code, the subshell exits 0, and the outer [ $? -eq 0 ] || DIE
never triggers. The caller receives a successful exit with partial
or corrupted output.
This is a known bug in bash 5.1's execute_cmd.c: when the first
command in a (... ) subshell is a builtin (cd), the executing subshell
path pollutes the builtin_ignoring_errexit state, causing
exit_immediately_on_error to be set to 0. Subsequent pipeline
failures are silently ignored. When the first command is a function
call, the execute_shell_function() path uses its own setjmp/longjmp
context that does not touch the errexit state, so pipeline failures
are caught normally. This was fixed in bash 5.2 (optimize_subshell_
command optimization) and documented in BashFAQ/105.
Fix by explicitly capturing the pipeline exit code with
pipeline_exit=0
cmd | other_cmd || pipeline_exit=$?
exit $pipeline_exit
inside each affected subshell, so the exit code propagates to the
outer guard regardless of set -e reliability.
Files changed:
initrd/bin/oem-factory-reset.sh
- hash generation subshell: find|xargs pipeline exit captured
before print_tree masks it
initrd/bin/root-hashes-gui.sh
- root hash computation subshell: find|xargs pipeline exit captured
initrd/bin/kexec-insert-key.sh
- initramfs cpio archive subshell: find|cpio pipeline exit captured
initrd/bin/inject_firmware.sh
- firmware initrd injection subshell: find|cpio pipeline exit captured
initrd/bin/kexec-save-default.sh
- default boot hash creation subshell: kexec-boot|xargs pipeline
exit captured
initrd/etc/luks-functions.sh
- DRK keyslot detection: process substitution <(cryptsetup|grep|sed)
silently loses the pipeline exit code. Replaced with an explicit
$(cryptsetup ...) || continue check that warns and skips the
container on failure.
Signed-off-by: Thierry Laurion <insurgo@riseup.net>
d16641e to
6ebc734
Compare
marmarek
left a comment
There was a problem hiding this comment.
Looks okay, and also I confirm it works with 4.19.4 -> 4.19.5 update: https://openqa.qubes-os.org/tests/187684#step/startup/5
Thanks a lot @marmarek. |
[Updated 2026-07-07: this description has been significantly revised since first opened. See commits and comments for full history.]
Remove the -u block that re-hashed file paths extracted from the old kexec_default_hashes.txt. Those paths are from a previously saved default boot entry. When a distro replaces a versioned boot file in-place (e.g. xen-4.19.4.gz to xen-4.19.5.gz), the old path no longer exists on disk and sha256sum fails.
The re-hash was redundant: the find ./ -type f ! -path "./kexec*" -print0 | xargs -0 sha256sum command in the same -u block already generates a complete, fresh hash of all current /boot files. The user re-saves the default boot entry from the boot menu to regenerate kexec_default_hashes.txt with current paths.
Root cause
The rehash also behaved unpredictably across bash builds. Before commit 1f6a975 (2025-02-07), the subshell had no TRACE_FUNC and set -e was not enforced on the pipeline failure: the sha256sum error was silently masked by print_tree returning 0, the subshell exited 0, and a partial hash file was signed. After 1f6a975 placed TRACE_FUNC as the first command inside the subshell, set -e became enforced, making the same pipeline failure fatally abort the subshell before print_tree could mask it.
This is a known bug in bash 5.1 execute_cmd.c: when the first command in a (... ) subshell is a builtin (cd), the executing subshell path pollutes the builtin_ignoring_errexit state, causing exit_immediately_on_error to be set to 0. When the first command is a function call, the execute_shell_function() path uses its own setjmp/longjmp context that does not touch the errexit state. Fixed in bash 5.2 and documented in BashFAQ/105.
Additional fixes
This PR also includes a second commit that hardens every (... ) subshell with a pipeline across the codebase against the same class of bug, using an explicit exit code capture pattern:
Files changed:
Mitigation on unpatched firmware
Then re-run Update checksums. With the file gone, the -e guard skips the re-hash entirely. Then set a new default from the boot menu to regenerate kexec_default_hashes.txt with current paths.