DF-4466 / verdict.json
{ "finding_id": "DF-4466", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Deterministic use-after-free (3/3 kernel panics, Fatal trap 12 supervisor write to 0x0 in mpipe_alloc_callback+0x1a2) plus permanent in-flight-bio hangs: dm-crypt completions run asynchronously holding only bio_caller_info1.ptr while suspend(no-op)/reload/resume destroys the table config with no drain. Root-triggerable via the routine LVM reload flow on busy volumes; freed config is zero-wiped (NULL write) or attacker-reclaimable (priv->ivgen->gen_iv indirect call) - memory-corruption class.", "exploit_chain": "priv UAF: (1) crypt->delay(12s) stack parks a read bio; (2) suspend+reload-with-fast-table+resume frees old config immediately (dm_table_destroy, no drain); (3) completion derefs freed config: zero-wiped chunk gives NULL-page write (observed panic); reclaimed chunk gives mpipe_alloc_callback on controlled mpipe then priv->ivgen->gen_iv indirect call and forged crypto session -> kernel code execution on the no-SMAP/SMEP/KASLR guest. Full uid=0 chain not completed (trigger itself requires /dev/mapper/control = root); no structural blocker documented in VERDICT.md.", "evidence": "run.log + panic.txt: Fatal trap 12, Stopped at mpipe_alloc_callback+0x1a2 (3/3 from fresh resets); ps_hang_variantD.txt: writers in D forever; fix_run.log: patched kernel drains (resume blocks 11s) and read returns correct 0x9e111cd8, no panic; VERDICT.md timeline.", "kernel_refs": [ "sys/dev/disk/dm/crypt/dm_target_crypt.c:693", "sys/dev/disk/dm/crypt/dm_target_crypt.c:750", "sys/dev/disk/dm/crypt/dm_target_crypt.c:607", "sys/dev/disk/dm/crypt/dm_target_crypt.c:976", "sys/dev/disk/dm/device-mapper.c:408", "sys/dev/disk/dm/device-mapper.c:550", "sys/dev/disk/dm/dm_ioctl.c:440", "sys/dev/disk/dm/dm_ioctl.c:508", "sys/dev/disk/dm/dm_table.c:129" ], "poc_changes": "Replaced the seeded sketch with a proplib-XML ioctl client (plistref ABI requires <plist> wrapper, hex integers); added warm-up read (open-time label probe eats one delay round); variant F reloads a FAST table to bypass the accidental disk_config() probe-drain that shields naive reload reproducers; 6 variants (A-F) developed to characterize which destroy paths race.", "attempts": 9, "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": 2380, "guest_dirty": 0, "build_cmd": "cc -O2 -o dmrace dmrace.c -lpthread", "run_cmd": "sh run.sh (variant F0)", "code_hash": "5ac343aac65dccb8bfa98ca7760b4fb69471b278c00fa01f2fe0cfbebfde1858", "notes": "INVARIANTS kernel. QEMU CPU has no AESNI (software crypto). Variant B (hammer+equal-table reload) is shielded by dmsetdiskinfo disk_config probe-wait only when the reloaded table is equally slow; with a fast reloaded table (normal lvextend case) the destroy proceeds while bios are in flight. is_open is a plain flag (device-mapper.c:212/232) - related core defect enabling remove-path exposure.", "recommended_fix": "Per-bio in-flight reference on the crypt config (nactive) incremented in strategy under the table lock and dropped at every completion endpoint; destroy drains nactive before mpipe/session teardown.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_kernel_uname": "DragonFly 6.5-DEVELOPMENT #1: Sun Sep 20 13:35:38 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_verdict": "fix.diff applied in-guest, nativekernel+installkernel, rebooted: exact F0 PoC no longer panics - resume now blocks ~11s draining the in-flight read (tsleep dmtcdn), read returns correct plaintext 0x9e111cd8 errno=0, config freed afterwards; variant B (36k writes, 10 reload cycles) clean; guest stable.", "fix_evidence": "fix_run.log (patched timeline: resume 13.0s->24.1s, reader correct), fix_build.log (full build, DONE=0), /boot/kernel/dm_target_crypt.ko contains dmtcdn ident." } |