Repository navigation
fix(raw): allow bootloader writes beside a mounted partition - #460
Closed
lucianogdittgen wants to merge 8 commits into
Closed
lucianogdittgen wants to merge 8 commits into
lucianogdittgen wants to merge 8 commits into
Conversation
Writing a bootloader to the gap ahead of the first partition of a disk was refused whenever any partition of that disk was mounted, as is always the case for the running rootfs: target device "/dev/mmcblk1" is in use, it holds the filesystem mounted at "/" The raw handler now checks only the region it writes, so partitions of a whole disk lying outside of it are ignored. The disk itself, its holders, overlapping partitions and partitions with unreadable bounds are still checked, as is any other target. raw-delta keeps the exclusive check, as its written length is not known up front. Signed-off-by: Luciano Dittgen <luciano.dittgen@ossystems.com.br>
The in-use check covers the region an object declares, from its size or, when compressed, its required-uncompressed-size, but the write itself went on for as long as the source or the decompressor produced data. Metadata understating the data let the write run into a partition the check had left out. The write now stops at the checked region and fails the install when the data goes past it. A compressed object not declaring its uncompressed size has no known region, so it keeps the exclusive check and is not bounded. Signed-off-by: Luciano Dittgen <luciano.dittgen@ossystems.com.br>
The region check only had its pure helpers tested. Exercise it against a loop device holding a mounted partition after a 1 MiB gap: a write to the gap is allowed, while one reaching into the partition, the whole disk and the partition itself are rejected. Signed-off-by: Luciano Dittgen <luciano.dittgen@ossystems.com.br>
The copy and tarball tests attached their loop device by hand and detached it only at the end of a successful run, so every failure left one attached. A missing mkfs.ext4 alone leaked one per test. They now use FakeLoopDevice, which detaches on drop. FakeLoopDevice::with_partition went on silently when the partition node never showed up, leaving the caller to fail later on an unrelated error. It now fails on the spot, detaching the device. Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
Bounding every write to the declared region turned understated metadata into failed installs. uhu takes required-uncompressed-size from `gzip -l`, which before gzip 1.12 reports it modulo 4 GiB, so images that always installed failed after partly writing their target. The bound also only stopped the write once it was under way, leaving the device partly written. The region only matters when the check let the write through beside a mounted partition, so only then is the write bounded. Whatever else is written whole, as before the region check. Beside a mount, the source is checked to fit before the device is opened, uncompressing it when compressed, and a larger one is refused untouched. Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
Only the region check itself was exercised on a device, never the raw installer going through it. Lay out an am335x on a loop disk, its rootfs partition mounted from 1 MiB: MLO and U-Boot install in the gap, plain and compressed, without touching the partition; an object reaching into it is refused; and one holding more data than declared is refused without writing. Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
The tests attaching loop devices format them as ext4, which failed for lack of mkfs.ext4 wherever the host did not happen to provide it. Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
These tests are ignored by default, and CI never ran them, so the in-use and region checks were only exercised on hardware. Build the test binary as the runner and run just it as root, keeping the cached build owned by the runner. The MTD tests stay out, as they load simulator modules. Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
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.
Problem
Since d6c8282 ("feat: refuse to install over a partition which is in use"), a
rawobject targeting a whole disk is refused whenever any partition of thatdisk is mounted. Boards keeping their bootloader in the gap ahead of the first
partition can no longer install a package updating it. On an am335x with MLO
at 128 KiB and U-Boot at 384 KiB of
/dev/mmcblk1, and the rootfs from 1 MiB:Neither write reaches a byte of the mounted filesystem. Agents up to 2.1.x
install these packages, so a device updated to 2.2.x loses the ability to
update its bootloader.
Changes
handler checks the region it writes (
Access::Region). On a whole disk,partitions lying entirely outside of it (bounds from sysfs
start/size)are ignored, along with whatever is stacked on them. These are still
checked: the disk itself, its holders, overlapping partitions and
partitions whose bounds cannot be read. Any other target uses the
existing check.
raw-deltakeeps the exclusive check, since its writtenlength is not known up front.
region comes from the object metadata, so the write is now bounded to it
and the install fails if the data goes further. A compressed object
without
required-uncompressed-sizehas no known region, so it keeps theexclusive check and is not bounded.
a partition placed after a 1 MiB gap and checks that a write to the gap is
allowed, while writes overlapping the partition, the whole disk and the
partition itself are refused.
Open question
The area outside every partition includes the partition table (sector 0, and
the backup GPT at the end of the disk), so writing it is now allowed while
partitions are mounted. The mounted filesystems are not corrupted, since the
kernel keeps its in-memory table, but the next boot may see a different
layout. That is what full-disk images need, but if you would rather refuse
writes reaching the partition table, I can add that.
Testing
cargo fmt --all -- --check,cargo clippy --locked --all-features --all --tests -- -D clippy::alland
cargo test --locked --all --all-featurespass.utils::fsonespass, including the new one, and so do the copy installer ones. The
flash, tarball, ubifs and mtd ones fail the same way on
masterin thatenvironment (they need
modprobe mtdram/nandsim).the new loop device test fail.
U-Boot as raw objects on
/dev/mmcblk1), the first commit installs thepackage with the rootfs mounted.