β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3186

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3186 Β· 10 files
FileTypeDescriptionSize
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.

VERDICT.md
↓ download raw

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

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_testable
↓ fix.diffper-fix-DF-3186

Confirmed 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.