{
  "finding_id": "DF-5024",
  "status": "reproduced",
  "reproduced": true,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "Sink-level reproduction on the stock guest kernel (DF-4986/DF-5012 house precedent): a kld module carrying the verbatim ioctl body of dfs_null.c ath_ioctl_phyerr() (lines 221-288, HAL calls elided -- they are unreachable on the leaking paths) demonstrates that the unconditional copyout at dfs_null.c:280 publishes the attacker-sized UNINITIALIZED kmalloc(outsize, M_TEMP, M_INTWAIT) block to userspace on BOTH error paths (unknown ioctl id -> default EINVAL; DFS_SET_THRESH with ad_in_size < 60 -> EINVAL). The ioctl returns EINVAL while 2048 bytes of recycled kernel M_TEMP land in the user buffer: 255/256 8-byte slots carry a marker previously sprayed into M_TEMP via sysctl, and the leading qword is malloc-chunk metadata -- kernel-heap provenance proven, stable across 3 runs. The real entry (SIOCGATHPHYERR, no privilege check on the path, if_ath_ioctl.c:301-302) is hardware-gated on this guest (no Atheros NIC, stock kernel lacks ATH_DIAGAPI; env.txt) but is unprivileged on any system with an ath interface and a DIAGAPI kernel -- arbitrary-size kernel heap disclosure (heap contents/pointers, KASLR defeat material). A/B with the fix pattern transplanted into the sink: EINVAL returned, buffer stays all-zero (marker8=0/256) -- leak eliminated; fix.diff also compile-validated against the real sys/ tree (ath_dfs.ko, -Werror clean).",
  "exploit_chain": "",
  "evidence": "run.log lines A:unknown-id: ioctl rc=-1 errno=22 ... marker8=255/256 and B:short-set ... (leak on both error paths, EINVAL returned); leak_sample.txt hexdump showing ffffffffffffffff allocator qword + DF5024!! (4446353032342121) fill; run_fixed_ab.log -- FIXED pattern: showing nonzero=0 marker8=0/256; fix_validation.log patched ath_dfs.ko build; env.txt hardware/config gating probe",
  "kernel_refs": [
    "sys/dev/netif/ath/ath_dfs/null/dfs_null.c:253",
    "sys/dev/netif/ath/ath_dfs/null/dfs_null.c:275",
    "sys/dev/netif/ath/ath_dfs/null/dfs_null.c:278",
    "sys/dev/netif/ath/ath_dfs/null/dfs_null.c:280",
    "sys/dev/netif/ath/ath/if_athioctl.h:187",
    "sys/dev/netif/ath/ath/if_athioctl.h:402",
    "sys/dev/netif/ath/ath/if_ath_ioctl.c:214",
    "sys/dev/netif/ath/ath/if_ath_ioctl.c:301"
  ],
  "poc_changes": "authored fresh from source analysis (no seed): sink module replicates the ioctl body line-for-line with the two HAL calls elided (unreachable on the leaking paths); marker-spray sysctl added to prove kernel-heap provenance of the disclosed bytes",
  "attempts": 2,
  "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": 240,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh (make KMOD df5024 + cc df5024_trigger.c) on the guest",
  "run_cmd": "kldload df5024.ko; sysctl debug.df5024_spray; df5024_trigger /dev/df5024 2048; kldunload df5024 (A/B: run_fixed_ab.log via df5024_fixed.c)",
  "code_hash": "e9c752d89d628c7db10a6717b893cd03012dcf3d501d41fa055cef3a2cae84ec",
  "notes": "NEW pass-2 finding, not a re-report: DF-2051 (GET_THRESH heap overflow) and DF-2052 (SET_THRESH NULL deref) live on the two known command ids success paths; this disclosure is on the error paths (unknown ids incl. DFS_RADARDETECTS=6, and short SET input) and works even on a kernel where GET/SET are never used. Severity Medium: unprivileged leak but entry needs an ath NIC + ATH_DIAGAPI kernel. Guest left clean (modules unloaded, scratch removed).",
  "recommended_fix": "Gate the clamp+copyout at dfs_null.c:278-281 on error == 0, mirroring ath_ioctl_diag() (if_ath_ioctl.c:214-222).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff (error-gated copyout) behaviorally validated at sink level (run_fixed_ab.log: EINVAL with all-zero buffer, marker8=0/256 vs 255/256 baseline) and compile-validated against the guests /usr/src copy (ath_dfs.ko module rebuilt -Werror clean, fix_validation.log; sources restored). The real ath entry path is hardware-gated on this guest, so the stock-kernel A/B is against the verbatim-pattern sink, not the driver itself.",
  "fix_evidence": "run_fixed_ab.log (A/B output); fix.diff; fix_validation.log (patched ath_dfs.ko build)"
}