Spectral scan start/configure permanently disables radar-detect hardware with no restore path β latent DFS-detection bypass (ar5416DisableRadar clobbers AR_PHY_RADAR_0_ENA/EXT and AR_RX_FILTER; ar5416StopSpectralScan restores none of it)
Summary
Prep runs DisableRadar on every configure/start/restore, Stop restores none of it, and the only re-enable callers are #if 0 in dfs_null.c - after any spectral scan, hardware radar detection stays off for the life of the channel. In-tree latent (null-DFS only + AR928x gating); on a real-DFS deployment it composes with the DF-2081 priv gap into an unpriv DFS-detection DoS. Save-and-restore fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5113 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| fix.diff | β | 1.6 KB | view raw | |
| manifest.json | β | 1.3 KB | view raw | |
| verdict.json | β | 3.0 KB | view raw |
DF-5113 β Spectral scan permanently disables radar-detect hardware; no restore path (Info / hardening)
File: sys/dev/netif/ath/ath_hal/ar5416/ar5416_spectral.c
Severity: Info (latent; see VERDICT.md) Β· Bucket: base:dev Β· Pass 2 (GLM 5.3)
What
ar5416PrepSpectralScan() (ar5416_spectral.c:75-81) is invoked on every
ar5416ConfigureSpectralScan() (:88), ar5416StartSpectralScan() (:193) and
ar5416RestoreSpectralConfig() (:234). It calls ar5416DisableRadar()
(:47-73), which:
- maxes the radar RSSI/height detection thresholds
(
AR_PHY_RADAR_0_RRSSI/AR_PHY_RADAR_0_HEIGHT, :56-61), - clears
AR_PHY_RADAR_0_ENAβ hardware radar detection OFF (:63), - clears
AR_PHY_RADAR_EXT_ENA(:67-68), - ORs bit 13 into
AR_RX_FILTER(:70-72) β never cleared either.
ar5416StopSpectralScan() (:205-218) clears only the spectral ENA/ACTIVE bits
and the AR_PHY_ERR_RADAR mask bit; it restores none of the radar state,
and no reachable in-tree path re-enables radar afterwards (the only
ath_hal_enabledfs() callers are inside #if 0 blocks in
sys/dev/netif/ath/ath_dfs/null/dfs_null.c:104-148).
Why Info (not higher)
- Reachable only on AR9280/AR9285/AR9287 NICs
(
halSpectralScanSupport = AH_TRUEonly in ar9280/9285/9287_attach.c; ar5416_attach.c:954 sets it AH_FALSE). - In this tree radar detection is non-operational anyway β the only DFS
module compiled is the null stub, whose
ath_dfs_radar_enable()body is#if 0'd out and returns 0 unconditionally. - On a stack with a real DFS module (upstream FreeBSD HAL lineage), an
unprivileged
SIOCGATHSPECTRAL(missing priv check, DF-2081)SPECTRAL_CONTROL_STARTon a DFS-channel AP would silently defeat radar detection until the next channel change β a regulatory/interference hazard, not a memory-safety bug.
PoC status
Not executed (skipped per contract β Info hardening finding). No PoC is
possible on the audit guest: it has no AR928x NIC, and the in-tree null-DFS
module makes the "disabled radar detection" state indistinguishable from the
never-enabled state. verdict.json carries status: "untested".
Recommended fix
ar5416StopSpectralScan() (or the driver, on scan stop) should restore the
AR_PHY_RADAR_0/AR_PHY_RADAR_EXT/AR_RX_FILTER values captured before
ar5416PrepSpectralScan() clobbered them β see fix.diff.
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
ssNULL-deref at :92 (ss->ss_fft_period) and the tail callar5416GetSpectralParams(ah, ss)at :143 are the sink of the already-filed DF-5070 chain (SPECTRAL_CONTROL_SET_PARAMSpasses a NULLindatawhenATH_DIAG_INis 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
- The audit guest has no AR9280/AR9285/AR9287 NIC β the only chips for
which
halSpectralScanSupportis AH_TRUE (ar9280_attach.c:904, ar9285_attach.c:558, ar9287_attach.c:457; ar5416_attach.c:954 sets AH_FALSE for everything else), soath_ioctl_spectral()returns EINVAL at if_ath_spectral.c:194-195 before reaching any of this code. - 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_SPECTRALexists (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 Kiwiss->ss_count == 128 β 0mutation (: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_RADARmask. The dead register reads at :200/:215 (value overwritten on the next line) and the duplicatedss_periodblock at :102-105 are cosmetic. - Dead code:
ar5416RestoreSpectralConfig()(:229-243) andar5416GetSpectralConfig()(:220-227) have noah_*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.
Fix verification
not_testablenot_testable: fix validation requires AR928x hardware and an operational DFS stack; the guest has neither. Patch is compile-reviewed only.
findings/poc/DF-5113/fix.diff
Confirmed kernel references
- sys/dev/netif/ath/ath_hal/ar5416/ar5416_spectral.c:47
- sys/dev/netif/ath/ath_hal/ar5416/ar5416_spectral.c:63
- sys/dev/netif/ath/ath_hal/ar5416/ar5416_spectral.c:70
- sys/dev/netif/ath/ath_hal/ar5416/ar5416_spectral.c:88
- sys/dev/netif/ath/ath_hal/ar5416/ar5416_spectral.c:193
- sys/dev/netif/ath/ath_hal/ar5416/ar5416_spectral.c:205
- sys/dev/netif/ath/ath_dfs/null/dfs_null.c:104
- sys/dev/netif/ath/ath/if_ath_spectral.c:194
Detail
Evidence (decisive lines)
findings/poc/DF-5113/VERDICT.md (path:line narrative); README.md; fix.diff (save/restore of AR_PHY_RADAR_0, AR_PHY_RADAR_EXT, AR_RX_FILTER in StopSpectralScan)
PoC changes
No PoC authored - Info hardening finding with no in-tree manifestation (no AR928x NIC on guest; radar detection never enabled in-tree).
Verified recommended fix
Save AR_PHY_RADAR_0/AR_PHY_RADAR_EXT/AR_RX_FILTER in ar5416PrepSpectralScan() and restore them in ar5416StopSpectralScan() - see fix.diff.
Verdict
ar5416PrepSpectralScan() (ar5416_spectral.c:75-81) runs ar5416DisableRadar() on every spectral configure/start/restore (:88, :193, :234); it maxes radar thresholds, clears AR_PHY_RADAR_0_ENA and AR_PHY_RADAR_EXT_ENA, and ORs bit 13 into AR_RX_FILTER (:47-73). ar5416StopSpectralScan() (:205-218) restores none of that state and the only in-tree re-enable path (ath_hal_enabledfs in ath_dfs/null/dfs_null.c:104-148) is #if 0'd out. On DFS-enabled deployments of this HAL lineage this silently defeats radar detection after any spectral scan; in this DragonFly tree the null-DFS module makes radar detection non-operational anyway, so the impact is latent - filed as Info hardening. PoC skipped per contract: the audit guest has no AR9280/9285/9287 NIC (halSpectralScanSupport is AH_TRUE only on those; ar5416_attach.c:954), so ath_ioctl_spectral() returns EINVAL at if_ath_spectral.c:194-195 before this file is reached.
No comments yet.