# DF-5113 VERDICT — spectral scan leaves radar-detect hardware disabled (no restore)

**Status: untested (Info hardening finding — PoC skipped per contract).**
**Reproduced: N/A · Impact: none in-tree (latent DFS-regulatory hazard upstream).**

## Narrative

Pass-2 audit of `sys/dev/netif/ath/ath_hal/ar5416/ar5416_spectral.c`
(244 LOC). Known-family items were excluded up front:

 * The `ss` NULL-deref at :92 (`ss->ss_fft_period`) and the tail call
   `ar5416GetSpectralParams(ah, ss)` at :143 are the sink of the already-filed
   **DF-5070** chain (`SPECTRAL_CONTROL_SET_PARAMS` passes a NULL `indata`
   when `ATH_DIAG_IN` is clear, if_ath_spectral.c:232-242). Not re-reported.
 * Driver-side GET/SET parameter copy bugs are **DF-2079** (GET_PARAMS
   out-buffer NULL/size mismatch), **DF-2080** (DYN leak),
   **DF-2081** (missing privilege check on SIOCGATHSPECTRAL). Not re-reported.

### The finding

`ar5416DisableRadar()` (ar5416_spectral.c:47-73) is reached via
`ar5416PrepSpectralScan()` (:75-81) from Configure (:88), Start (:193) and
Restore (:234). It maxes detection thresholds and clears
`AR_PHY_RADAR_0_ENA` + `AR_PHY_RADAR_EXT_ENA` (radar detection OFF) and ORs
bit 13 into `AR_RX_FILTER`. `ar5416StopSpectralScan()` (:205-218) restores
none of this; the only in-tree re-enable path (`ath_hal_enabledfs()`) is
inside `#if 0` in `sys/dev/netif/ath/ath_dfs/null/dfs_null.c:104-148`.
So after any spectral configure/start, radar-detect hardware stays disabled
for the life of the channel — an asymmetric state clobber with no rollback.

### Why not verified on the guest

1. The audit guest has no AR9280/AR9285/AR9287 NIC — the only chips for
   which `halSpectralScanSupport` is AH_TRUE (ar9280_attach.c:904,
   ar9285_attach.c:558, ar9287_attach.c:457; ar5416_attach.c:954 sets
   AH_FALSE for everything else), so `ath_ioctl_spectral()` returns EINVAL
   at if_ath_spectral.c:194-195 before reaching any of this code.
2. Even with the NIC, this tree's radar detection is never enabled (null DFS
   stub, body `#if 0`'d), so there is no observable "before" state to lose —
   the bug is latent, manifesting only in DFS-enabled deployments (the
   upstream FreeBSD HAL lineage compiled with a real DFS module).

Per the audit contract, Low/Info hardening findings skip the QEMU phase;
`verdict.json` records `status: "untested"`, `impact: "none"`.

### Negative result (what else was hunted and killed in this file)

 * **Get-params copy sizes / NULL** (ar5416GetSpectralParams :150-168): fills
   only 5 of the ~36 bytes of `HAL_SPECTRAL_PARAM`; every driver call site
   passes a memset/kzalloc'd destination (if_ath_spectral.c:226-228,
   :240-241, ath_spectral_attach :117-128). No uninit leak from this file.
 * **RX phyerr samples**: no in-tree consumer of `HAL_PHYERR_SPECTRAL`
   exists (grep across sys/dev/netif/ath) — the FFT-sample surface is dead
   in this tree.
 * **Config register writes** (:90-141): every field write goes through
   `SM()` (`((_v) << _f##_S) & _f`, ah_internal.h:579) which masks to the
   field width; register addresses are fixed constants. The Kiwi
   `ss->ss_count == 128 → 0` mutation (:125-126) only rewrites the caller's
   kernel copy. No OOB, no div-by-zero (no division in the file).
 * **Disable pairs/flags**: Start (:196-202) vs Stop (:209-217) are symmetric
   for ENA/ACTIVE + `AR_PHY_ERR_RADAR` mask. The dead register reads at
   :200/:215 (value overwritten on the next line) and the duplicated
   `ss_period` block at :102-105 are cosmetic.
 * **Dead code**: `ar5416RestoreSpectralConfig()` (:229-243) and
   `ar5416GetSpectralConfig()` (:220-227) have no `ah_*` method slot
   (ah.h:1528-1534 registers only Configure/GetConfig/Start/Stop/
   IsEnabled/IsActive) and zero callers tree-wide — orphaned.

## Fix

See `fix.diff`: capture `AR_PHY_RADAR_0`/`AR_PHY_RADAR_EXT`/`AR_RX_FILTER`
on first prep and restore them in `ar5416StopSpectralScan()`.
`fix_status: not_testable` — behavioral fix on hardware we do not have;
the patch is compile-reviewed only.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
