{
  "finding_id": "DF-2534",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "Both cited missing-lifetime-lock defects confirmed against the real functions (verbatim sha256-gated xdisk.c compiled into the guest kernel). (1) xa_size:833-835 NULL-checks si_drv1 then dereferences sc->info.d_media_blocks with no lock while xa_terminate_check NULLs si_drv1 (:665) and kfree(sc)s (:680) under xdisk_lk. Hammer race with the real xa_size on cpu0 and the real xa_terminate_check on cpu1: 157,024,256 reader iterations vs 51,550,501 frees (100 percent same-address slab reuse), 664 UAF READS captured, each proven by observing 0xC0FFEE00DF2534, a value planted only AFTER the kfree plus reallocation, so the deref provably landed on freed heap through the stale pointer. Production window quantified: diskpsize re-dispatches psize to the raw device (DSO_RAWPSIZE set by xdisk at :491; subr_disk.c:1273) and dev_doperate has no drain, so psize genuinely races teardown; primitive is an attacker-groomable 8-byte fixed-offset kernel heap disclosure returned as the device size (info leak, Low ceiling, no write/control-flow primitive, no uid=0 chain). (2) xa_strategy:801/805 has no NULL check at all: with si_drv1 NULL it write-faults at 0x90 in devstat_start_transaction (Fatal trap 12, supervisor write) - demonstrated with fabricated state and honestly labeled post-drain-only in production because disk_destroy at :663 drains strategy before si_drv1 is NULLed at :665. Fix takes xdisk_lk and re-checks si_drv1 under it in both functions; A/B validated: 0 UAF reads over 82M iterations on the patched kernel and clean ENXIO biodone from strategy.",
  "exploit_chain": "Root creates an xa device via dmsg; any holder of the /dev/xa%d node can race size queries (dssize/DIOCGPART paths reach xa_size via diskpsize raw re-dispatch) against span-loss teardown. The reader captures sc from si_drv1 before xa_terminate_check NULLs it, then dereferences after the free: it reads whatever now occupies the freed M_XDISK slab slot (8 bytes at offsetof d_media_blocks) and that value is returned to userspace as the device size. Grooming the freed slot with a chosen kernel object converts this into a repeatable fixed-offset heap info leak. Read-only primitive; escalation ceiling is information disclosure, not code execution.",
  "evidence": "run.log (DF2534-RACE summary: 157024256 iterations, 664 UAF READS CAPTURED; DF2534-S Fatal trap 12 at 0x90 in devstat_start_transaction+0xa), panic.txt (full trap dump), run.patched.log (0 UAF reads; xa_strategy returned 0 with biodone b_error=6 b_flags=0x800), fix_build.log, env.txt",
  "kernel_refs": [
    "sys/dev/disk/xdisk/xdisk.c:801",
    "sys/dev/disk/xdisk/xdisk.c:805",
    "sys/dev/disk/xdisk/xdisk.c:833",
    "sys/dev/disk/xdisk/xdisk.c:835",
    "sys/dev/disk/xdisk/xdisk.c:665",
    "sys/dev/disk/xdisk/xdisk.c:680",
    "sys/kern/subr_disk.c:1273",
    "sys/dev/disk/xdisk/xdisk.c:491"
  ],
  "poc_changes": "Authored the race harness fresh (no seed): magic-value method (plant 0xC0FFEE00DF2534 into the freed slot after same-address reallocation; si_drv1 never points at the reused block, so observation cannot false-positive). Two bring-up fixes: lwkt_yield() did not prevent CPU starvation under the cooperative LWKT scheduler (spinning kthread never gives the cpu back to user threads) - replaced with a 1-tick tsleep every 256K iterations; first frozen run still captured 3 UAF reads before the wedge (serial_console.baseline.log), final run completed with the full summary and the guest healthy.",
  "attempts": 4,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #2: Mon Oct 19 20:55:04 UTC 2026  root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 5400,
  "guest_dirty": 0,
  "build_cmd": "cd findings/poc/DF-2533 && ./build.sh   (shared harness; sha256 gate 58b0ed1672b5fb63f7d484a7fae6ba04484372c962e13d9c573b69790cfa55c9; kernel-compiled)",
  "run_cmd": "dfbsd-qemu/vm.sh run_root sysctl -w debug.df2534_race=12 ; (fresh boot) sysctl -w debug.df2534_strat_null=1   (panics; capture via vm.sh log); patched build: same triggers, 0 hits, no fault",
  "code_hash": "dd2289882cb7b55b3f57fdd81c548be2ef1ad7add4aa04ed6e811419aaaee03c",
  "notes": "Impact set to leak (the production-reachable primitive is the UAF read); the strategy NULL write-fault panic is demonstrated but requires beating the disk_destroy drain, so it is quantified as defense-in-depth evidence. Patched-kernel race iteration counts are lower (82M vs 157M) because fixed xa_size serializes on xdisk_lk, as expected.",
  "recommended_fix": "Take xdisk_lk and re-check si_drv1 under it in xa_size and xa_strategy (queue the bio before dropping the lock), matching the lifetime lock xa_terminate_check already uses."
}
