⬢ DragonFlyBSD Kernel Audit
DF-2655 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2655",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "REPRODUCED on the stock INVARIANTS kernel: a ZLIB-compressed DATA block whose XXH64 check (and the whole chain up to the volume header iCRCs) is valid but whose payload is not a zlib stream mounts cleanly and, on read(2), returns kernel-fabricated zeros as SUCCESS (dd exit 0, first 64KB all zero, md5 b1e63222a373994ee242ce1a7e3da0fc vs original 876569bdd45df2756a69a3fe85660f2a, dmesg shows HAMMER2 ZLIB Fatal error during decompression). Deterministic across three independent mounts. impact is recorded as panic because the same missing-b_error bug family in the LZ4 sibling callback (hammer2_strategy.c:215-216) additionally produced an attacker-length OOB kernel write: bzero(bp->b_data + result, ...) with result negative; |result| = stored size prefix + 1 chosen via the image; with -16381 the write began at bp->b_data-16381 in an unmapped page causing Fatal trap 12 at memset+0xf0 and a full machine halt (panic.txt); with -9 it silently corrupted 9 bytes of mapped heap adjacent to the 64KB buffer. The substitution itself (the DF-2655 claim) is an integrity attack (Medium, malicious-media precondition: root mounts crafted image, no unprivileged or remote trigger). fix.diff (both callbacks: B_ERROR + EIO + b_resid=b_bcount + early return) validated A/B on rebuilt kernel: pristine images bit-identical, zlib/lz4-small/lz4-big patched images now read(2)=EIO with no panic.",
  "exploit_chain": "craft hammer2 image with compressible file -> offline patch one stored ZLIB payload to 0xFF and recompute its XXH64 plus every ancestor check up to the volume header iCRCs (h2zsub) -> root mounts image (vfs.usermount=0) -> any read(2) of the file -> kernel decompression fails -> callback returns fabricated zeros with b_resid=0 and no b_error -> caller receives silent data substitution (no error, no warning). LZ4 variant of the same construction additionally drives an OOB zeroing write before a 64KB kernel buffer with attacker-chosen length up to the stored block size. No privilege escalation path exists on these paths: impact ceiling is integrity substitution plus the LZ4 OOB write/panic.",
  "evidence": "run.b.patched.log (substitution proof: dd exit 0, md5 b1e63222..., first 64KB zero, kprintf), run.a.pristine.log (md5 876569bd...), panic.txt (Fatal trap 12 at memset+0xf0, fault VA 0xfffff80062a22003 = bp->b_data-16381), lz4small-run.log (LZ4 substitution + silent 9-byte OOB), patch.zlib.log (checksum cascade), fixrun1.log/fixrun2.log (fixed kernel: EIO everywhere, pristine bit-identical, no panic)",
  "kernel_refs": [
    "sys/vfs/hammer2/hammer2_strategy.c:257-261",
    "sys/vfs/hammer2/hammer2_strategy.c:262-270",
    "sys/vfs/hammer2/hammer2_strategy.c:206-220",
    "sys/vfs/hammer2/hammer2_strategy.c:215-216",
    "sys/vfs/hammer2/hammer2_chain.c:5380-5428",
    "sys/vfs/hammer2/hammer2_lz4.c:515-517"
  ],
  "poc_changes": "No runnable seed existed: wrote the full trigger machinery (h2zsub.c) - a recursive on-media walker resolving names exactly like hammer2_xop_nresolve (DIRENT check.buf name -> embed.dirent.inum -> INODE bref keyed by inum), verifying the original XXH64 before patching, corrupting the stored payload, and cascading recomputed XXH64 checks up through the indirect/inode chain into every valid volume header iCRC. Guest quirks handled: vn device nodes are devfs-clone allocated (vnconfig -c vn file prints the unit); ro mounts used for read stability.",
  "attempts": 9,
  "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": 420,
  "guest_dirty": 0,
  "build_cmd": "./build.sh",
  "run_cmd": "./mkimage.sh && ./run_stock_ab.sh && ./run_lz4.sh small && ./run_lz4.sh big",
  "code_hash": "4fb1e27842880ac0fadac4ff91d8339e695367763a4bf44b9aa66efc2be68d21",
  "notes": "LZ4 OOB write (strategy.c:215-216, negative result into bzero length/offset) is a DISTINCT memory-safety defect fully evidenced in this pack - recommend the orchestrator file it as a new finding (High: kernel heap OOB write, crafted media, attacker-chosen length, panic reproduced). fix.diff already covers it via the early return. The K1 fix kernel also demonstrated the OOB is gone (lz4-big -> EIO, no panic). fix_status: fixed (A/B validated, k1build.log BUILD_RC=0).",
  "recommended_fix": "On decompression failure in both hammer2_decompress_LZ4_callback and hammer2_decompress_ZLIB_callback set B_ERROR, b_error=EIO, b_resid=b_bcount and return early before any bcopy/tail-fill (see fix.diff)",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Tue Oct 20 03:15:00 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 validated on rebuilt kernel K1: pristine zfile/lzfile reads bit-identical (md5 876569bd...); zlib-patched, lz4-small and lz4-big patched images now read(2)=EIO (dd: Input/output error) with zero panics - the lz4-big operation that halted the stock kernel is cleanly failed; kprintf retained for operators (fixrun1.log, fixrun2.log)",
  "fix_evidence": "fixrun1.log (F1 pristine md5s identical; F2 zlib EIO), fixrun2.log (F3 lz4-small EIO; F4 lz4-big EIO no panic), k1build.log BUILD_RC=0"
}