hammer_upgrade_undo_4 never terminates on UNDO_ALIGN-incongruent alloc_offset in a crafted volume header β unkillable kernel thread under finalize_lock+sync_lock, mount hang and buffer-cache deadlock
Summary
The version<4->>=4 UNDO reinitialization loop steps next_offset from HAMMER_ENCODE_UNDO(0) in fixed 512 increments and terminates only on exact equality with undomap->alloc_offset (:381/:403). alloc_offset comes straight from the on-disk root volume header and is never validated for UNDO_ALIGN congruence anywhere: hammer_load_volume checks only signature/fsid/vol_no and stage1 checks only first/last <= alloc - a crafted header trivially satisfies (first=last=HAMMER_ENCODE_UNDO(0), alloc=HAMMER_ENCODE_UNDO(0x500)). With alloc not congruent to 512, equality is unreachable and the loop spins forever, hammer_bnew'ing and dirtying DUMMY records while the caller holds finalize_lock and sync_lock exclusively (hammer_ioctl.c:638-639): every filesystem operation on the mount blocks forever, the thread is unkillable, and once dirty undo buffers exhaust the buffer cache the bnew path blocks waiting for a flusher that can never run (it needs the very sync_lock the spinning thread holds) - a system-wide buffer-cache deadlock. Reachable only via HAMMERIOC_SET_VERSION (SYSCAP_NOVFS_IOCTL) on a RW mount of a crafted/corrupted image - privileged robustness gap, not a privilege boundary. Loop non-termination arithmetic-certain + reachability traced end-to-end (repro recipe in finding); Phase V skipped per contract (Low). Fix: validate alloc_offset zone/alignment/minimum before the loop (or at mount). [VERIFIED pass2: reachability REFUTED - mount-time undo recovery rejects non-congruent alloc_offset EIO before SET_VERSION; loop arithmetic real, defense-in-depth only]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3186 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| patch_undo.c | β | 2.5 KB | view raw | |
| run.log | β | 1.5 KB | view raw | |
| README.md | β | 650 B | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| build.sh | β | 227 B | view raw | |
| run.sh | β | 148 B | view raw | |
| manifest.json | β | 774 B | view raw | |
| verdict.json | β | 2.2 KB | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| env.txt | β | 272 B | view raw |
DF-3186 -- verification pack
Finding: hammer_upgrade_undo_4 loop cannot terminate on incongruent alloc_offset
Status: not_reproduced / impact denial_of_service / confidence certain
What was run
guest: cc -O -o patch_undo patch_undo.c; image: newfs_hammer, first mount clean, umount, patch version=3 + alloc skew, remount
How to reproduce
guest: mount_hammer /dev/vn0 /mnt/h1 (expect EIO: Failed to recover)
Expected
mount rejects the incongruent alloc_offset with EIO; SET_VERSION unreachable
See VERDICT.md for the full narrative and verdict.json for the persisted machine verdict. Artifacts: patch_undo.c, run.log.
DF-3186 verification
Kernel: DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64
Bottom line
Loop arithmetic confirmed, trigger REFUTED on this guest: patching vol_version=3 and a non-512-congruent UNDO alloc_offset into a clean newfs_hammer image makes the RW mount FAIL with EIO before HAMMERIOC_SET_VERSION can run - hammer_recover_stage1 replays the undo stream up to alloc_offset, hits the unparseable tail (FIFO record bad head signature / Illegal UNDO TAIL signature) and the mount aborts with Failed to recover HAMMER filesystem. Since every on-media undo record is 512-aligned and alloc_offset only advances in aligned units, the non-congruent precondition cannot exist on a mountable volume; the non-terminating while-loop in hammer_upgrade_undo_4 (reachable only through the EROFS-gated SET_VERSION ioctl on a mounted fs) is therefore defense-in-depth only. Fix (alignment validation at mount) authored and compile-validated.
Kernel references
- sys/vfs/hammer/hammer_undo.c:381-405
- sys/vfs/hammer/hammer_ioctl.c:627-641
- sys/vfs/hammer/hammer_recover.c:228-243
Exploit chain
(none: availability/correctness finding; no privilege chain)
Fix
validate UNDO alloc_offset alignment at mount
See fix.diff in this directory (git-apply-able against the read-only sys/ tree; applied only inside the disposable verification guest).
Reproduction
build: guest: cc -O -o patch_undo patch_undo.c; image: newfs_hammer, first mount clean, umount, patch version=3 + alloc skew, remount
run: guest: mount_hammer /dev/vn0 /mnt/h1 (expect EIO: Failed to recover)
expected: mount rejects the incongruent alloc_offset with EIO; SET_VERSION unreachable
Notes on the PoC (what changed vs the finding sketch)
first patch variant computed the skew from first_offset (0xFFFF... sentinel on a fresh image) producing an invalid alloc and an unrelated EIO; the second variant anchors on next_offset and the recovery rejection is then exercised exactly
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
run.log: patch_undo output, mount EIO, and the recovery dmesg lines; patch_undo.c
PoC changes
first patch variant computed the skew from first_offset (0xFFFF... sentinel on a fresh image) producing an invalid alloc and an unrelated EIO; the second variant anchors on next_offset and the recovery rejection is then exercised exactly
Verified recommended fix
validate UNDO alloc_offset alignment at mount
Verdict
Loop arithmetic confirmed, trigger REFUTED on this guest: patching vol_version=3 and a non-512-congruent UNDO alloc_offset into a clean newfs_hammer image makes the RW mount FAIL with EIO before HAMMERIOC_SET_VERSION can run - hammer_recover_stage1 replays the undo stream up to alloc_offset, hits the unparseable tail (FIFO record bad head signature / Illegal UNDO TAIL signature) and the mount aborts with Failed to recover HAMMER filesystem. Since every on-media undo record is 512-aligned and alloc_offset only advances in aligned units, the non-congruent precondition cannot exist on a mountable volume; the non-terminating while-loop in hammer_upgrade_undo_4 (reachable only through the EROFS-gated SET_VERSION ioctl on a mounted fs) is therefore defense-in-depth only. Fix (alignment validation at mount) authored and compile-validated.
No comments yet.