# DF-5024 — ath_ioctl_phyerr() uninitialized-heap disclosure on error paths (dfs_null.c)

**File:** `sys/dev/netif/ath/ath_dfs/null/dfs_null.c` (pass 2)
**Lines:** 253-281 (allocation at :253, unconditional copyout at :280)
**Class:** kernel heap info leak (CWE-908 / CWE-200) — NEW in pass 2;
not a re-report of DF-2051 (GET_THRESH overflow) or DF-2052 (SET_THRESH NULL
deref), which live on the *success* paths of the two known command ids.

## Build (on the DragonFly guest, as root)

```
sh build.sh          # builds df5024.ko (vulnerable-pattern sink) + df5024_trigger
```

## Run

```
sh run.sh            # sprays marker through M_TEMP, then exercises the leak 3x
sh run_ab-style:     # see run_fixed_ab.log — A/B against df5024_fixed.c (fix applied)
```

## Expected output (decisive lines from run.log)

```
A:unknown-id: ioctl rc=-1 errno=22(Invalid argument) nonzero=1 marker8=255/256
  first64: ffffffffffffffff 4446353032342121 4446353032342121 ...
B:short-set:  ioctl rc=-1 errno=22(Invalid argument) nonzero=1 marker8=255/256
```

* The ioctl FAILS with EINVAL — yet the user buffer contains 2048 bytes of
  kernel heap: `4446353032342121` is the ASCII marker `DF5024!!` that the
  sysctl spray wrote into `M_TEMP` and freed; the leading `ffffffffffffffff`
  qword is malloc-zone chunk metadata.
* 255/256 marker slots — the entire `kmalloc(outsize, M_TEMP, M_INTWAIT)`
  block from dfs_null.c:253 crossed the kernel/user boundary despite the
  command being rejected.
* Stable across 3 runs (run.log); with the fix pattern applied
  (df5024_fixed.c / run_fixed_ab.log) the same ioctls return EINVAL and the
  buffer stays all-zero (`marker8=0/256`) — leak eliminated.

## Why a sink module

The guest has no Atheros NIC and the stock kernel is built without
ATH_DIAGAPI, so `SIOCGATHPHYERR` cannot be entered at runtime (env.txt).
Following the DF-4986/DF-5012 house precedent, `df5024.c` carries the
**verbatim ioctl body of `ath_ioctl_phyerr()`** (dfs_null.c:221-288) with only
the two HAL calls elided — they are unreachable on the leaking paths, which
`break`/`default` before any `sc->sc_ah` dereference. The kmalloc/copyout
behavior being demonstrated is entirely kernel-generic (M_TEMP + copyout).

## Artifacts

* `df5024.c` — sink, vulnerable pattern (verbatim structure)
* `df5024_fixed.c` — same sink with the fix's `error == 0` gate
* `df5024_trigger.c` — user trigger (unknown-id + short-SET variants)
* `run.log`, `run_fixed_ab.log`, `leak_sample.txt`, `build.log`, `env.txt`
* `fix.diff` — the real fix against `sys/` (error-gated copyout)
