Sysctl read handlers unlock before touching the HAL and never test sc_invalid β racing device_detach frees the HAL object and unmaps the BARs before the oids are removed, giving an unprivileged reader a use-after-free panic
Summary
All dev.ath.N.* leaves are CTLTYPE_INT|RW with reads needing no privilege. Handlers take ATH_LOCK for one call, RELEASE it, then call ath_hal_get* with no lock and no sc_invalid test (10 sites). device_detach runs ath_detach - which frees the HAL object and DMA - and only AFTERWARDS removes the sysctl oids; the PCI detach then unmaps the register BARs. An unprivileged reader inside that window dereferences freed HAL memory (and possible unmapped MMIO) -> UAF panic. Trigger: unpriv sysctl read loop racing a root detach. Pack carries the full lifetime trace. Guard + hold-lock fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4374 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 3.7 KB | β raw | |
| trigger_df4374.sh | β | 1.4 KB | view raw | |
| build.sh | β | 92 B | view raw | |
| run.sh | β | 214 B | view raw | |
| build.log | β | 79 B | view raw | |
| run.log | β | 79 B | view raw | |
| env.txt | β | 131 B | view raw | |
| fix.diff | β | 861 B | view raw | |
| verdict.json | β | 3.3 KB | view raw | |
| manifest.json | β | 1.4 KB | view raw |
DF-4374 β ath sysctl read handlers race driver detach (UAF / unmapped MMIO read)
What
The dev.ath.N.* sysctl read handlers in
sys/dev/netif/ath/ath/if_ath_sysctl.c are world-readable, and most of them
call into the HAL outside ATH_LOCK with no sc_invalid check and no
lifetime guarantee on sc/sc_ah:
ath_sysctl_txantennaβ power-set under lock (:256-258), HAL read at :260 unlockedath_sysctl_diversityβ :316-318 lock, :320 unlocked HAL readath_sysctl_diagβ :347-349 lock, :351 unlockedath_hal_getdiagath_sysctl_tpscale:376-378/:380,ath_sysctl_tpc:403-405/:407,ath_sysctl_rfkill:430-432/:434,ath_sysctl_rfsilent:563-565/:567,ath_sysctl_tpack:602-604/:606,ath_sysctl_tpcts:627-629/:631,ath_sysctl_intmit:652-654/:656 β same pattern.
During driver detach, ath_detach() frees sc->sc_ah (ath_hal_detach,
if_ath.c:1436) and the PCI layer unmaps the register BARs, all before
device_sysctl_fini() (subr_bus.c:2151, called from device_detach only
after DEVICE_DETACH returns at :2141) removes the sysctl oids. The per-oid
oid_lock (kern_sysctl.c:1460-1482) protects the oid, not the softc:
a reader that entered a handler before detach began continues to run while
the hardware context is torn down β dereference of freed ath_hal state or
an MMIO read from unmapped space β kernel page fault / UAF read β panic.
The read side needs no privilege (sysctl -n dev.ath.0.txantenna as any
user); the detach side is privileged (kldunload if_ath, devctl-driven PCI
hot-remove, CardBus eject).
Build / Run (probe)
./run.shβ unprivileged probe: checks fordev.ath.0.*; if present, runs a reader storm intended to be raced againstkldunload if_athby root. On the audit QEMU guest there is no ath hardware, so it reports absence (see run.log).
Expected on real hardware
With the reader storm running as an unprivileged user and root executing
kldunload if_ath (repeatedly), the kernel panics with a page fault in
ar5212RegRead/OS_REG_READ or a UAF read of sc_ah β timing-dependent,
typically within tens of detach attempts.
Files
trigger_df4374.shβ reader-storm probeVERDICT.mdβ lifetime analysis with citationsfix.diffβ hold ATH_LOCK across the HAL interaction and bail out when the device is invalid (representative for all affected handlers)
DF-4374 VERDICT β sysctl read handlers vs ath driver detach lifetime race
Status: not_reproduced β no Atheros hardware in the audit QEMU guest
(dev.ath tree absent, no 0x168c PCI device). Lifetime analysis verified
from source; the race is real but the detach side is privileged, so impact
ceiling is a race-condition kernel panic (DoS).
Reproduction attempt (honest)
- Guest: DragonFly 6.5-DEVELOPMENT,
vtnet0 lo0only;sysctl -n dev.ath.0.slottimeβ "unknown oid 'dev.ath'" as both root and user (run.log).pciconf -lshows no Atheros (0x168c) device. trigger_df4374.shexecuted: reports the absent device and exits.- Cannot execute the race without ath(4) hardware β
not_reproduced, impact none observed on this guest.
Why the race is real (citations)
-
Unprivileged read surface. All
dev.ath.N.*leaves are created withSYSCTL_ADD_PROC(..., CTLTYPE_INT | CTLFLAG_RW, ...). Reads need no privilege; only writes are gated (kern_sysctl.c:1446-1447:caps_priv_check(td->td_ucred, SYSCAP_NOSYSCTL_WR)unless CTLFLAG_ANYBODY). Any local user can enter these handlers. -
Handlers unlock before touching the HAL. Representative:
ath_sysctl_txantennatakes ATH_LOCK forath_power_set_power_state(if_ath_sysctl.c:256-258), releases it, then callsath_hal_getantennaswitch(sc->sc_ah)at :260 with no lock and nosc_invalidtest. Same pattern at :320, :351, :380, :407, :434, :567, :606, :631, :656. (slottime/acktimeout/ctstimeout/rxantenna do the read under ATH_LOCK but are still exposed to the softc lifetime problem below.) -
Driver detach destroys the hardware context while oids are live.
device_detach(subr_bus.c:2131-2152) runsDEVICE_DETACHβ for ath:ath_detach(if_ath.c:1385-1438) stops the chip, frees DMA, and callsath_hal_detach(sc->sc_ah)at :1436 (frees the HAL object) β and only afterwards callsdevice_sysctl_fini(dev)at :2151, which is what removes/frees the dev.ath.N.* oids. The PCI detach then unmaps the register BARs. -
The sysctl lock protects the oid, not the softc. Handlers execute under the per-oid lockmgr lock (kern_sysctl.c:1460-1482; shared for reads).
device_sysctl_fini/sysctl_ctx_freemust take the exclusive oid lock, so it waits for readers β but that only prevents oid UAF; it does nothing to stop ath_detach from freeingsc_ahwhile a reader is parked between its ATH_UNLOCK and its HAL call. -
Result. Reader at :260 during/after
ath_hal_detach: - readsah->ah_sc-adjacent state from freed memory (UAF read), and/or - executesOS_REG_READ(ah, ...)through a bus-space handle whose BAR mapping was torn down β kernel-mode access to unmapped device space β page fault β panic.
Impact
Race-condition local DoS (kernel panic). The reader side is unprivileged but the detach side requires root (kldunload if_ath) or physical hot-removal β per the audit rubric this is Low ("narrow trigger ... requires specific hardware or already-privileged user" for the triggering half).
Fix validation
Not possible on this guest (no ath hardware). fix.diff shows the pattern:
check sc_invalid under ATH_LOCK and keep the lock across the HAL
interaction so ath_detach's ATH_LOCK-serialized teardown cannot proceed
underneath the reader; apply the same shape to every listed handler.
fix_status: not_testable (hardware absent).
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_testablefix.diff shows the representative corrected ath_sysctl_txantenna (git-apply-able, verified); the same shape must be applied to diversity/diag/tpscale/tpc/rfkill/rfsilent/tpack/tpcts/intmit. Cannot validate on the guest (no ath hardware).
fix.diff; VERDICT.md 'Fix validation' section
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (4-step lifetime proof with citations); run.log + env.txt (guest: dev.ath unknown oid, NO_ATHEROS_PCI, unprivileged uid=1001 probe); fix.diff (representative ATH_LOCK + sc_invalid pattern, git-apply-able); README.md
PoC changes
Trigger is a shell reader-storm (sysctl(8)) raced against root kldunload; no C needed. Guest probe reports hardware absence.
Verified recommended fix
In every affected handler, take ATH_LOCK, bail with ENXIO when sc->sc_invalid, and keep the lock across the HAL interaction (pattern in fix.diff).
Verdict
Lifetime race between world-readable dev.ath.N.* sysctl read handlers and ath(4) driver detach. Most handlers (txantenna if_ath_sysctl.c:260, diversity :320, diag :351, tpscale :380, tpc :407, rfkill :434, rfsilent :567, tpack :606, tpcts :631, intmit :656) call into the HAL outside ATH_LOCK with no sc_invalid check; ath_detach frees sc_ah at if_ath.c:1436 and the PCI layer unmaps the register BARs before device_sysctl_fini (subr_bus.c:2151) removes the sysctl oids, and the per-oid sysctl lock (kern_sysctl.c:1460-1482) protects only the oid, not the softc. An unprivileged reader parked between ATH_UNLOCK and the HAL call during root-initiated detach (kldunload if_ath / hot-remove) dereferences freed HAL state or reads unmapped MMIO -> kernel page fault panic. Reader side needs no privilege (writes are gated by SYSCAP_NOSYSCTL_WR per kern_sysctl.c:1446-1447, reads are not). Not executed: the audit QEMU guest has no Atheros hardware (dev.ath absent, no 0x168c PCI device); probe run honestly reports absence. Impact ceiling: race-condition local DoS; detach side is privileged, hence severity Low.
No comments yet.