bootloader/lkenv: fix same kernel occupying multiple boot partitions - #17420
Open
kubiko wants to merge 1 commit into
Open
bootloader/lkenv: fix same kernel occupying multiple boot partitions#17420kubiko wants to merge 1 commit into
kubiko wants to merge 1 commit into
Conversation
findFreeBootPartition() checked whether the requested value was already assigned to a boot image partition inside the same loop that looks for a free one. Whichever case matched first in boot image matrix order won, so when an earlier partition looked free it was returned without ever examining the later partition that already held the value. The caller then went on to assign the value to that partition too, leaving the very same kernel revision recorded in both: bootimg_matrix [boa][pi-kernel_387.snap] bootimg_matrix [bob][pi-kernel_387.snap] The guard therefore only worked while the value sat in the first row of the matrix. That is always the case at image build time, which is the situation the existing comment describes, but not in the steady state at run time, where the installed kernel alternates between boot image partitions on every refresh. Re-extracting the assets of the currently installed kernel - on a revert, on undo of a failed refresh or when re-seeding - then duplicates it whenever it happens to sit in a later row, which is why a device can be updated a number of times before this shows up. The consequence is permanent. With the same kernel in every boot image partition and snap_kernel reserved, no free boot image partition can be found at all and every subsequent kernel refresh fails with "cannot find free boot image partition". Split the check into its own pass over the whole matrix, before any partition is considered free, so that a value which is already assigned always resolves to the partition holding it regardless of its position. The pre-pass keeps its own empty label check so an empty value can never match an unset row. This affects the kernel matrix of v1 and v2 run environments, and the recovery system matrix of v2 recovery environments, where it leaks a boot image partition per duplicate rather than wedging the device, because all assigned recovery systems are reserved. The bug has been present since the lk bootloader was introduced in 2.42. Signed-off-by: Ondrej Kubik <ondrej.kubik@canonical.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #17420 +/- ##
=======================================
Coverage 78.81% 78.81%
=======================================
Files 1406 1408 +2
Lines 197084 197103 +19
Branches 2498 2498
=======================================
+ Hits 155330 155350 +20
- Misses 32387 32396 +9
+ Partials 9367 9357 -10
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Thu Jul 30 18:07:41 UTC 2026 Failures:Preparing:
Executing:
Restoring:
Skipped tests from snapd-testing-skipIf you wish to have any of the below tests run in your PR, in your PR description, add 'unskip:' followed by a copy-and-pasted list of the below tests you wish to run (unskip plus test list must be valid yaml)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
findFreeBootPartition()checked whether the requested value was already assigned to a boot image partition inside the same loop that looks for a free one. Whichever case matched first in boot image matrix order won, so when an earlier partition looked free, it was returned without ever examining the later partition that already held the value. The caller then went on to assign the value to that partition too, leaving the very same kernel revision recorded in both:Why it isn't hit every time
The guard therefore only worked while the value sat in the first row of the matrix. That is always the case at image build time, which is the situation the existing comment describes, but not in the steady state at run time, where the installed kernel alternates between boot image partitions on every refresh. Re-extracting the assets of the currently installed kernel - on a revert, on undo of a failed refresh or when re-seeding - then duplicates it whenever it happens to sit in a later row(b slot), which is why a device can be updated a number of times before this shows up.
Impact
The consequence is permanent. With the same kernel in every boot image partition and
snap_kernelreserved, no free boot image partition can be found at all and every subsequent kernel refresh fails withFix
Split the check into its own pass over the whole matrix, before any partition is considered free, so that a value which is already assigned always resolves to the partition holding it regardless of its position. The pre-pass keeps its own empty label check so an empty value can never match an unset row.
This affects the kernel matrix of v1 and v2 run environments, and the recovery system matrix of v2 recovery environments, where it leaks a boot image partition per duplicate rather than wedging the device, because all assigned recovery systems are reserved.
Tests
Two tests added to stress this scenario, fails without the fix, pass when fix is applied.
Note
This stops new duplicates from appearing, but does not recover devices which already hit this scenario.
This is addressed in a separate PR: #17419