Skip to content

fix(fde): initialize a full snapshot chunk when wiping the COW header - #145

Merged
imlk0 merged 4 commits into
masterfrom
fix/fde-cow-chunk-init
Oct 9, 2026
Merged

imlk0 merged 4 commits into
masterfrom
fix/fde-cow-chunk-init

Conversation

@imlk0

@imlk0 imlk0 commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Fixes #141: disk-persist + integrity=true failed on the FIRST boot with INTEGRITY AEAD ERROR, sector 8 and Failed to create dm-snapshot device.

Root cause (two parts)

  1. Partial chunk init. wipe_cow_device_header_async wrote only 4 KiB of zeros, but the persistent snapshot table uses a 16-sector (8 KiB) chunk and dm-snap-persistent reads whole chunks. Sector 8, the first sector beyond the 4-KiB wipe, had no valid integrity tag (only sectors written through the mapping carry one, since the LUKS2 format path never initializes the data-area tags), so the snapshot's header read failed and dmsetup create returned I/O error.
  2. blkid misreporting. libblkid's end-of-device probe read also fails on those uninitialized-tag sectors, and it turns the I/O error into "no signatures". On an existing volume this made the guest wipe COW metadata that was actually present, destroying the previous boot's delta. Verified on disk: after a clean reboot the COW header holds a valid SnAp header while blkid still reports clean.

Changes

  • SNAPSHOT_CHUNK_SIZE_SECTORS is now a constant shared by the snapshot table and the wipe; the wipe writes a full chunk so the entire header region carries valid tags.
  • The persistent-mode wipe is gated on the header chunk itself: unreadable aborts (fail closed), non-zero content is kept for the snapshot to interpret (valid SnAp metadata is reused; anything else makes dmsetup fail closed), all zeros mean blank and get initialized.
  • A freshly created delta skips the probe and initializes the header chunk directly (blank by construction).
  • The fsck-141 CI scenario now re-boots the same persistent overlay (issue fde: disk-persist with integrity=true fails first boot when creating persistent dm-snapshot #141's requested first-boot + same-disk second-boot test), and the boot test no longer treats a bare INTEGRITY AEAD ERROR console line as fatal (blkid's end-of-device probe always produces those on a fresh delta).

Verification

Unit tests (cargo test -p cryptpilot-fde --lib, 11 pass) cover the full-chunk wipe, the fail-closed read check, and non-zero header preservation. End-to-end in QEMU (alinux3 image, disk-persist + integrity=true, UKI, fixed 0.9.2 guest RPM):

scenario before after
first boot (fresh delta) INTEGRITY AEAD ERROR, sector 8, emergency mode login prompt; full chunk initialized
second boot after clean reboot n/a (first boot failed) COW header has existing content, keeping it -> metadata reused -> login
second boot after forced stop n/a header reads blank -> re-initialized -> login

cargo fmt --check, make clippy, shellcheck clean.

Refs: #140 was fixed by #142; the fsck-140 CI scenario is green on master. This PR makes fsck-141 green too.

@ostest-bot

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@ostest-bot

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start 。

@ostest-bot

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@ostest-bot

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start 。

@ostest-bot

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@ostest-bot

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start 。

imlk0 added 4 commits October 9, 2026 10:28
dm-snap-persistent reads and writes whole chunks, so wiping only the
first 4 KiB of the COW device left sector 8 (the first sector beyond
the wipe) without a valid integrity tag. The snapshot's header read
then failed with "INTEGRITY AEAD ERROR, sector 8" and the dm-snapshot
creation failed on the first boot of a disk-persist + integrity=true
delta. The chunk size is now a constant shared by the wipe and the
snapshot table, and the wipe writes a full chunk so the whole header
region carries valid tags.

blkid's "no signatures" is not proof of a blank device: its
end-of-device probe read fails on a delta whose integrity tags were
never written, and libblkid turns that failure into "clean". On an
existing volume that report caused a wipe of the previous boot's COW
metadata. The persistent-mode wipe now reads the header chunk itself:
unreadable aborts (fail closed), non-zero content is kept for the
snapshot to interpret (valid SnAp metadata gets reused, anything else
makes dmsetup fail closed), all zeros mean blank.

On a freshly created delta the COW is blank by construction, so the
probe is skipped and the header chunk is initialized directly.

Fixes #141.

Assisted-by: Claude:glm-5.3
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
The fsck-141 scenario now re-boots the same persistent overlay after
the first boot reaches login, covering the issue #141 request for a
first-boot plus same-disk second-boot test: the second boot must keep
and reuse the persisted COW metadata.

Also stop treating a bare "INTEGRITY AEAD ERROR" console line as a
boot failure. blkid probing the end of a freshly created delta always
triggers those errors (sectors whose tags were never written), so the
marker made the test fail on a healthy boot. The fatal markers are the
dmsetup failure, emergency mode, or the FDE service failing.

Assisted-by: Claude:glm-5.3
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
The direct qemu boot path skipped the container-runtime setup that the
qemus-entrypoint path performs. On alinux4 docker is the real Docker
Engine and the test container has no init system, so dockerd was never
running and the QEMU container exited instantly, failing both persist
scenarios within seconds. Run the same ensure_docker_runtime step and,
when dockerd was started by us (no bridge configured), run the QEMU
container with the host network namespace so guest user-mode networking
works.

Assisted-by: Claude:glm-5.3
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
The persist overlay was created in the workdir root while the converted
image lives in the per-test subdirectory, so the overlay's bare relative
backing filename never resolved and qemu exited instantly on the missing
file (the failure was swallowed because the function runs inside an if
condition, leaving only an unhelpful "container exited unexpectedly").
Create the overlay in the image's directory, where the boot container's
single mount covers both files, and fail loudly when overlay creation
fails.

Assisted-by: Claude:glm-5.3
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
@imlk0
imlk0 force-pushed the fix/fde-cow-chunk-init branch from 0a5a4b7 to 6ce9c51 Compare October 9, 2026 02:28
@imlk0
imlk0 merged commit 6ce9c51 into master Oct 9, 2026
@ostest-bot

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@ostest-bot

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start 。

@imlk0
imlk0 deleted the fix/fde-cow-chunk-init branch October 9, 2026 02:29
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.

fde: disk-persist with integrity=true fails first boot when creating persistent dm-snapshot

2 participants