DF-3167 / verdict.json
{ "finding_id": "DF-3167", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "hammer_reblock_data() bcopy() consumes the on-disk leaf data_len/data_offset with no production validation; for data_len>16KB the blockmap allocator only enforces the big-block boundary, so with the (naturally misaligned) META append cursor the copy writes up to 16368 attacker-controlled bytes past the end of a getblkd 64KB kernel buffer. Demonstrated first-shot on the stock INVARIANTS guest: forged direntry (data_len=65536, CRC-forged, source=bigfile A extent) -> Fatal trap 12 supervisor-write in memmove+0x10a at hammer_reblock.c:487, guest dead. Primitive = controlled kernel heap overflow from a crafted HAMMER1 image triggered by the root-run hammer reblock; silent corruption when the adjacent buffer KVA is mapped (the fault only occurs on unmapped pages). Precondition: privileged mount + privileged ioctl (same threat model as DF-3033/DF-3076/DF-3148), so not an unpriv->root path; ceiling = kernel memory corruption / host crash / potential code exec from a malicious image. Fixed and validated in-guest.", "exploit_chain": "crafted HAMMER1 image (single forged B-Tree leaf elm: direntry with data_offset -> attacker-known A extent, data_len=65536, forged data_crc + node crc) -> root mounts image rw -> hammer reblock (default free_level=0) -> hammer_reblock_helper selects record -> hammer_btree_extract_data passes CRC gate -> hammer_alloc_data allocates 65536B in META zone at misaligned cursor xoff -> bcopy writes [xoff,xoff+65536) into 64KB buffer -> xoff bytes OOB write of controlled data -> observed: page fault (unmapped neighbor) = panic; with mapped neighbor: silent kernel heap corruption (victim: adjacent buffer-cache objects, no KASLR/SMAP on guest). uid0 escalation N/A (trigger already requires root).", "evidence": [ "run.log (serial console: getfree of forged a000000022000000, D-line, Fatal trap 12 supervisor write at memmove+0x10a)", "panic.txt (trap block)", "VERDICT.md (geometry: xoff=0x1390=4912 bytes past buffer end)", "fixrun.log (fixed kernel: HAMMER: reblock a000000022000000/65536: illegal data range, EINVAL, 0 Fatal traps)", "build.log (patcher self-validation: node CRC algo, elm match)" ], "kernel_refs": [ "sys/vfs/hammer/hammer_reblock.c:487", "sys/vfs/hammer/hammer_reblock.c:464", "sys/vfs/hammer/hammer_reblock.c:467", "sys/vfs/hammer/hammer_btree.c:736", "sys/vfs/hammer/hammer_ondisk.c:1634", "sys/vfs/hammer/hammer_ondisk.c:1116", "sys/vfs/hammer/hammer_blockmap.c:177", "sys/vfs/hammer/hammer_blockmap.c:372" ], "poc_changes": "PoC authored fresh for this finding (no seed). Required guest work: vnconfig WITHOUT -T (which truncates the backing file); image >=10GB sparse on /build; locate leaf elm via hammer -f /dev/vn0 show; patcher self-validates via node-CRC recomputation and source-pattern check; CRC32C (v7 iscsi) implemented in C.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 4800, "guest_dirty": 0, "build_cmd": "in-guest: cc -O2 -Wall -o /tmp/patch_leaf /tmp/patch_leaf.c", "run_cmd": "in-guest as root: sh mkimage.sh; /tmp/patch_leaf /build/poc.img; sh trigger.sh", "code_hash": "572d63c3da479432065458184b0e570868e489bf0b5f77cd072c89c84707bdf1", "notes": "Impact recorded as panic (what manifested); the underlying primitive is a controlled OOB heap write (silent corruption when adjacent KVA mapped). Also killed by the same guard at this call site: data_len>64KB getblk-panic variant (DF-3033 class), buffer-crossing small-record OOB read, data_len==0-with-data_offset NULL-deref path, and the >16KB source getblk buffer-overlap panic (bgetvp) observed during fix-v1 iteration. Legit records never cross a 16KB buffer, so the guard is a no-op on uncorrupted filesystems.", "recommended_fix": "hammer_reblock_data(): reject records whose (data_offset,data_len) is not fully contained in one 16KB buffer (data_len<=0 || data_len>HAMMER_BUFSIZE || (data_offset&BUFMASK)+data_len>HAMMER_BUFSIZE -> EINVAL); plus buffer-align >16KB allocations in hammer_blockmap_alloc()", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Mon Sep 7 12:28:26 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to in-guest /usr/src, make -j6 nativekernel + installkernel, reboot into #1 Sep 7 12:28:26. Exact PoC re-run: HAMMER: reblock a000000022000000/65536: illegal data range on console, hammer reblock returns Invalid argument, NO trap, guest up, clean unmount, 0 Fatal trap in /var/log/messages. Baseline (stock #0 Jul 2) fatal trap reproduced in the same session.", "fix_evidence": "fixrun.log (patched-run transcript + dmesg), fix.diff, VERDICT.md fix section" } |