⬢ DragonFlyBSD Kernel Audit
DF-3149 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3149",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "One HAMMERIOC_MIRROR_WRITE with two mrecs (REC creating a well-formed record create_tid=T delete_tid=0; PASS with same base and delete_tid=T) panics the stock INVARIANTS kernel at hammer_delete_at_cursor() KKASSERT(delete_tid != create_tid) (hammer_object.c:2501-2502). hammer_mirror_check (mirror.c:777-786) approves any wire delete_tid != 0 differing from the on-disk one and hammer_mirror_update (mirror.c:814-833) passes it unvalidated; the ADJUST path stores the tid first, then asserts. Production (non-INVARIANTS) kernels store delete_tid==create_tid - the duplicate-record B-Tree invariant violation hammer_object.c:2496-2502 explicitly warns about. Needs no crafted creation: any existing slave record whose create_tid the master knows is killable with a single PASS mrec. fix.diff (reject delete_tid <= cursor leaf create_tid) validated: record 1 still created (count=80), mrec 2 rejected EINVAL, no panic.",
  "exploit_chain": "gated ioctl or malicious mirror-master stream -> HAMMERIOC_MIRROR_WRITE -> write_rec/write_pass -> hammer_mirror_check approves -> hammer_mirror_update -> hammer_delete_at_cursor(ADJUST, wire delete_tid) -> stores delete_tid==create_tid (hammer_object.c:2468) -> KKASSERT (hammer_object.c:2501-2502) panic",
  "evidence": [
    "panic.txt: console panic + backtrace hammer_delete_at_cursor<-hammer_ioc_mirror_write",
    "run.log: baseline two-mrec trigger (ssh dies, guest down)",
    "run.fixed.log: patched kernel creates mrec1 (count=80) then rejects mrec2 with error 22, no panic",
    "fix.diff: validated kernel fix"
  ],
  "kernel_refs": [
    "sys/vfs/hammer/hammer_mirror.c:777",
    "sys/vfs/hammer/hammer_mirror.c:814",
    "sys/vfs/hammer/hammer_mirror.c:828",
    "sys/vfs/hammer/hammer_mirror.c:625",
    "sys/vfs/hammer/hammer_mirror.c:696",
    "sys/vfs/hammer/hammer_object.c:2459",
    "sys/vfs/hammer/hammer_object.c:2468",
    "sys/vfs/hammer/hammer_object.c:2501"
  ],
  "poc_changes": "Same harness as DF-3148; duptid mode sends REC(btype=R) + PASS(delete_tid==create_tid) in one 160-byte ubuf",
  "attempts": 2,
  "guest_uname": "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",
  "runtime_sec": 70,
  "guest_dirty": 0,
  "build_cmd": "cc -O -o /tmp/mirror_poc mirror_poc.c",
  "run_cmd": "/tmp/mirror_poc /mnt/f1 duptid   (fresh 2GB newfs_hammer on vn0 mounted at /mnt)",
  "code_hash": "4b94e084cfbf563d1409e503e025609a43f52a9bd48992fdf68e1e296f2ba3a6",
  "notes": "Guest panic line is hammer_object.c:2502 (assert spans 2501-2502 in audited tree). Non-INVARIANTS impact is the stored duplicate-record state, not a panic.",
  "recommended_fix": "In hammer_mirror_update reject mrec->leaf.base.delete_tid <= cursor->leaf->base.create_tid (see fix.diff).",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Mon Sep  7 05:10:35 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Same patched build as DF-3148 (fix.diff includes the update-path guard): identical PoC on fresh fs creates mrec1s record (count=80) and aborts with head.error=22 on mrec2; no panic; guest up.",
  "fix_evidence": "run.fixed.log; patched-kernel uname #1 (2026-09-07)"
}