β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4882

Reachable KASSERT panic in ath_rate_ctl_start() from a zero-length Supported Rates IE beacon β€” remote DoS on default (INVARIANTS) DragonFly kernels with ath+onoe+IBSS

Summary

DFly's DEFAULT kernel ships INVARIANTS (verified in the running guest) making this KASSERT a production panic: a beacon with IBSS capinfo and a zero-length RATES IE passes element verification (checks maxlen only), the rate-setup error is discarded, and ic_newassoc reaches the assert - the code's own comment documents rate-less adhoc nodes as 'not fatal'. One crafted beacon = kernel panic from radio range. amrr.c:312 carries the identical assert (twin flagged). Graceful-return fix (+ mirror). [PASS-2 NOTE: amrr.c:312 carries the identical reachable KASSERT - apply the same graceful-return fix there (see findings/poc/DF-4882/fix.diff)]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4882 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.9 KB ↓ raw
VERDICT.md β€” 2.4 KB ↓ raw
env.txt β€” 1.1 KB view raw
fix.diff β€” 836 B view raw
verdict.json β€” 3.4 KB view raw

DF-4882 β€” onoe ath_rate_ctl_start() reachable KASSERT panic via rate-less IBSS beacon

What

sys/dev/netif/ath/ath_rate/onoe/onoe.c:285:

KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));

DragonFly's default kernel config ships options INVARIANTS (sys/config/X86_64_GENERIC:56, verified in the running guest), so KASSERT is an active panic in production. A single unauthenticated 802.11 beacon/probe-response with a zero-length Supported Rates IE (and IBSS capability bit), received by an ath(4) interface running in adhoc (IBSS) mode with the onoe ratectl module attached, reaches this KASSERT and panics the kernel.

Full static chain (every hop verified in-tree)

  1. adhoc_recv_mgmt processes BEACON/PROBE_RESP frames β€” unauthenticated (sys/netproto/802_11/wlan/ieee80211_adhoc.c:678-699).
  2. ieee80211_parse_beacon only flags a missing RATES element (IEEE80211_BPARSE_RATES_INVALID, ieee80211_input.c:667-668) β€” a zero-length RATES IE passes IEEE80211_VERIFY_ELEMENT (checks ie[1] > maxlen only β€” ieee80211_input.h:31-41; 0 <= 15) with clean status.
  3. IBSS capinfo + unknown addr2 β†’ ieee80211_add_neighbor (ieee80211_adhoc.c:727-735).
  4. ieee80211_init_neighbor β†’ ieee80211_setup_rates(ni, sp->rates, ...) with rates[1] == 0 β†’ rs_nrates = 0 β†’ ieee80211_fix_rate loop body never executes β†’ okrate == 0 β†’ returns error (sys/netproto/802_11/wlan/ieee80211_proto.c:682,746-748) β€” and the return value is ignored (ieee80211_node.c:1559-1561).
  5. ieee80211_add_neighbor proceeds unconditionally to ic->ic_newassoc(ni, 1) (ieee80211_node.c:1607-1608).
  6. ath_newassoc β†’ ath_rate_newassoc(sc, an, isnew) (sys/dev/netif/ath/ath/if_ath.c:1271, ~ath_newassoc body).
  7. onoe.c:189-190 isnew β†’ ath_rate_ctl_start β†’ KASSERT panic (onoe.c:285). The very next lines (onoe.c:316-321) explicitly document that nodes without a rate set are expected here ("orphaned nodes created in adhoc mode may not have any rate set ... This is not fatal") β€” but on INVARIANTS kernels the KASSERT fires first and it is fatal.

The amrr sibling has the identical KASSERT (amrr.c:312, filed separately as context only; this pack covers onoe).

Threat model

Unauthenticated radio-range peer (IBSS participant or spoofer of the cell's BSSID) β†’ kernel panic = remote DoS. Preconditions: Atheros NIC + onoe ratectl module attached (sample is the shipped default, X86_64_GENERIC:266) + IBSS/adhoc vap + INVARIANTS kernel (the default).

Why not reproduced on the QEMU guest

The guest has no Atheros NIC (pciconf shows only a virtio vgapci) and no onoe.ko module (env.txt). The chain is proven statically end-to-end above.

Fix

Replace the KASSERT with the graceful fallback the surrounding code already implements (drop the rate-ctl start for empty rate sets), matching the code's own comment that this case "is not fatal". See fix.diff.

VERDICT.md
↓ download raw

DF-4882 VERDICT

Status: inconclusive (guest lacks the required hardware; static chain proven end-to-end; no kernel manifestation possible on this guest).

What was verified

  • Guest kernel: DragonFly dfbsd 6.5-DEVELOPMENT #0 .../sys/X86_64_GENERIC β€” the shipped default config includes options INVARIANTS (verified at /usr/src/sys/config/X86_64_GENERIC:56 in the guest), so KASSERT is an active panic, not a debug nicety. This is the decisive environmental fact for severity: the assertion is production-reachable.
  • Guest hardware: pciconf -l shows only vgapci0@pci0:0:2:0 chip=0x11111234 (virtio-gpu). No Atheros (168c:) device, no radio.
  • Guest modules: /boot/kernel contains if_ath.ko, ath_hal.ko, ath_rate.ko (strings identify it as sample, the default), and wlan_amrr.ko. No onoe module is shipped.
  • Source audit chain (README.md) β€” every hop cited path:line; nothing relies on speculation. The empty-rate-set scenario is acknowledged by the code itself (onoe.c:316-320 "orphaned nodes created in adhoc mode may not have any rate set ... This is not fatal"), and ieee80211_setup_rates's error return for zero rates (ieee80211_proto.c:746-748) is demonstrably ignored at ieee80211_node.c:1559-1561.

Why it cannot be exercised here

Triggering requires an ath(4) interface in IBSS mode with onoe attached and an injected 802.11 beacon β€” none of which QEMU/virtio can provide (no NIC, no 802.11 stack attachment, module absent). Building onoe.ko in the guest would not help: ic_newassoc is only wired to onoe code when an ath device attaches through it.

Impact ceiling

Remote kernel panic (DoS) of the default INVARIANTS kernel, from one crafted frame, for ath+onoe+IBSS configurations. amrr.c:312 has the same bug (out of this file's scope).

Fix validation

Not possible on this guest (no ath hardware). fix.diff authored against the read-only tree: converts the KASSERT into the graceful path the surrounding code already implements. fix_status: not_applicable.

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_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored (KASSERT -> graceful return for rs_nrates==0, matching the code's own 'not fatal' comment) but not kernel-buildable-testable here: the panic path needs an ath NIC in IBSS mode with onoe attached, which the guest cannot provide.

findings/poc/DF-4882/fix.diff
↓ fix.diffper-fix-DF-4882

Confirmed kernel references

Detail

Exploit chain

unauthenticated radio peer -> beacon/probe-resp with IBSS capinfo + zero-length RATES IE -> adhoc_recv_mgmt (ieee80211_adhoc.c:735) -> ieee80211_add_neighbor -> ic_newassoc(ni,1) (ieee80211_node.c:1607) -> ath_rate_newassoc isnew (onoe.c:187-190) -> ath_rate_ctl_start -> KASSERT panic (onoe.c:285). Cannot be chained further: it is a clean panic.

Evidence (decisive lines)

README.md (full hop-by-hop chain with path:line), VERDICT.md (environment facts), env.txt (guest: INVARIANTS at X86_64_GENERIC:56, pciconf shows no 168c device, /boot/kernel has no onoe module). Decisive lines: onoe.c:285 KASSERT; ieee80211_node.c:1607-1608 unconditional ic_newassoc; ieee80211_proto.c:746-748 okrate==0 error path ignored.

PoC changes

n/a - no runnable PoC possible without ath hardware; evidence pack documents the static chain and guest environment instead

Verified recommended fix

Replace onoe.c:285 KASSERT with an early return when rs_nrates==0 (the path ath_rate_update already handles gracefully), and mirror in amrr.c:312

Verdict

Reachable-assertion panic, proven statically end-to-end but not exercisable on this guest (no Atheros NIC, no onoe module). DragonFly's default kernel config ships INVARIANTS (verified at /usr/src/sys/config/X86_64_GENERIC:56 in the running guest), so onoe.c:285 KASSERT is an active panic: an unauthenticated IBSS beacon with a zero-length Supported Rates IE reaches ic_newassoc(ni,1) with rs_nrates==0 because ieee80211_parse_beacon accepts zero-length RATES elements (ieee80211_input.h:31-41 checks ie[1] > maxlen only) and ieee80211_init_neighbor ignores the ieee80211_setup_rates error return (ieee80211_node.c:1559-1561, ieee80211_proto.c:746-748). The code's own comment (onoe.c:316-320) documents that rate-less adhoc nodes reach this path and claims it is 'not fatal' - on INVARIANTS kernels it is. Remote DoS for ath+onoe+IBSS configurations.