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

ieee80211_beacon_update: first app-IE install does pointer arithmetic on NULL bo->bo_appie and memcpy's the IE to near-NULL kernel address (guaranteed panic)

Summary

ieee80211_beacon_construct() records bo->bo_appie/bo_appie_len only when iv_appie_beacon != NULL at construct time. If an app IE is installed later (SIOCS80211 IOC_APPIE subtype BEACON - the standard hostapd/WPS beacon-IE plumbing on a running AP), the APPIE block in ieee80211_beacon_update() executes 'bo->bo_appie += adjust' on NULL and add_appie() memcpy's ie_len bytes to address 0x0+ie_len - a guaranteed fatal supervisor write. VERIFIED on stock INVARIANTS guest (software net80211 radio KLD with real-driver beacon discipline + setappie): 'Fatal trap 12, fault VA = 0x40 (= NULL + 64-byte IE), supervisor write, Stopped at memcpy+0x1c' within 100ms of the ioctl, guest down; fix validated (first-app-IE case handled explicitly): trigger clean. Privileged local DoS (SYSCAP_NONET_WIFI); reachable via ordinary hostapd/WPS operation - any AP whose beacon predates its first beacon app IE (the common ordering) panics on the first TBTT after the daemon pushes the IE; WPS state changes make this remotely inducible in WPS-enabled deployments. No uid0 route (write confined to the unmapped zero page).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3282 Β· 14 files
FileTypeDescriptionSize
README.md β€” 2.5 KB ↓ raw
VERDICT.md β€” 3.7 KB ↓ raw
verdict.json β€” 4.1 KB view raw
manifest.json β€” 912 B view raw
fw.c β€” 9.7 KB view raw
Makefile β€” 177 B ↓ download
setappie.c β€” 1.6 KB view raw
build.sh β€” 87 B view raw
run.sh β€” 111 B view raw
build.log β€” 5.6 KB view raw
run.log β€” 1020 B view raw
panic.txt β€” 2.0 KB view raw
env.txt β€” 256 B view raw
fix.diff β€” 973 B view raw

DF-3282 β€” ieee80211_beacon_update: NULL bo->bo_appie pointer arithmetic writes app IE to near-NULL kernel address (panic)

File: sys/netproto/802_11/wlan/ieee80211_output.c (pass 2 audit)

What

ieee80211_beacon_construct() (line 3215-3219) records bo->bo_appie/bo->bo_appie_len only when vap->iv_appie_beacon != NULL at beacon-construction time. If a beacon application IE is installed later (SIOCS80211 IEEE80211_IOC_APPIE with subtype BEACON β€” exactly what hostapd does when WPS is enabled/disabled on a running AP, or when any app IE blob is pushed after the AP came up), ieee80211_beacon_update()'s APPIE block (lines 3591-3613) computes

int adjust = aielen - bo->bo_appie_len;   /* 0 -> 64 */
bcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust, bo->bo_tim_trailer_len);
bo->bo_tim_trailer += adjust;
bo->bo_appie += adjust;                    /* NULL + 64 == 0x40 */
...
frm = add_appie(bo->bo_appie, aie);        /* memcpy to 0x40 */

bo->bo_appie is NULL (never set), so the IE is memcpy'd to kernel address 0x0+ie_len β€” a guaranteed fatal page fault on every execution. Reproduced on the stock INVARIANTS guest:

fw: wlan0 RUN, beacon allocated          <- beacon built with no app IE
setting 64-byte beacon app IE on wlan0... (SIOCS80211 APPIE, root)
Fatal trap 12: page fault while in kernel mode
fault virtual address   = 0x40
fault code      = supervisor write data, page not present
Stopped at      memcpy+0x1c:    movq    %rdx,(%rdi)

Build / run (repo root)

sh findings/poc/DF-3282/build.sh     # builds /root/fw/fw.ko + setappie in guest
sh findings/poc/DF-3282/run.sh       # baseline: panics within ~100ms of ioctl

The harness (fw.c) is a minimal software net80211 radio: registers an ieee80211com ("fw0") with hostap support, allocates the beacon via ieee80211_beacon_alloc() on RUN and services ieee80211_beacon_update() from a 100 ms callout β€” the same discipline as a real MAC driver at TBTT.

Expected output

Baseline (stock kernel): panic/fatal trap 12, fault VA = ie_len (0x40), guest down (see panic.txt).

Patched (fix.diff applied, kernel rebuilt): ioctl succeeds, beacon keeps updating (dmesg shows continued beacon ticks, ifconfig wlan0 destroy works, guest stays up). See VERDICT.md.

Threat

Privileged local DoS (SIOCS80211 requires SYSCAP_NONET_WIFI). Reachable through the standard hostapd/WPS beacon-IE plumbing on any running AP whose beacon predates the first app IE β€” the common ordering.

VERDICT.md
↓ download raw

DF-3282 VERDICT β€” VERIFIED / reproduced / fixed

Bottom line

Reproduced: yes. Impact: panic (privileged local DoS). Status after fix: fixed.

ieee80211_beacon_update() writes a newly-installed beacon application IE to kernel address 0x0+ie_len whenever the first app IE is installed on a vap whose beacon was constructed without one. The root cause is pointer arithmetic on a NULL bo->bo_appie:

  • sys/netproto/802_11/wlan/ieee80211_output.c:3215-3219 β€” ieee80211_beacon_construct() only records bo->bo_appie when vap->iv_appie_beacon != NULL at construct time (beacons are built once, at vap RUN).
  • sys/netproto/802_11/wlan/ieee80211_output.c:3599-3612 β€” on the first update after an app IE is installed (SIOCS80211 IEEE80211_IOC_APPIE, subtype BEACON β€” the standard hostapd/WPS beacon-IE plumbing), the code does bo->bo_appie += adjust with bo->bo_appie == NULL and then add_appie() memcpy's ie_len bytes to that address.

How it was reproduced (stock INVARIENTS kernel, #0 Jul 2 06:02:54)

Harness: fw.c β€” a ~350-line software net80211 radio (KLD) that registers ieee80211com "fw0", supports hostap, allocates the beacon with ieee80211_beacon_alloc() on RUN and services ieee80211_beacon_update() from a 100 ms callout (real-driver discipline). No wifi hardware needed.

kldload fw.ko
ifconfig wlan create wlandev fw0 wlanmode ap
ifconfig wlan0 ssid fwtest mode 11b channel 1 up
   -> "fw: wlan0 RUN, beacon allocated"        (beacon built, NO app IE)
./setappie wlan0                                (SIOCS80211 APPIE, 64B vendor IE)
   -> <=100 ms later:
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x40            <-- NULL + 64 (exactly the IE length)
fault code = supervisor write data, page not present
Stopped at memcpy+0x1c: movq %rdx,(%rdi)     <-- add_appie's memcpy

Full serial-console capture in panic.txt; command transcript in run.log. Guest down after the fault (vm.sh status => down).

Exploit chain

None beyond denial of service: the write address is 0x0+N with N = ie_len ≀ 1024, always inside the unmapped zero page on x86-64. It is a hard kernel panic (CWE-476/682 family), not a controllable-corruption primitive.

Fix validation

fix.diff makes the update path handle the first-app-IE case explicitly: the new IE occupies the space vacated at the head of the shifted trailer and becomes part of the trailer (so later TIM resizes move it too).

  • Baseline (stock kernel): panic within one beacon tick β€” see panic.txt.
  • Patched (patch -p1 < fix.diff + make nativekernel && make installkernel, kernel #1 Sep 10 04:41): setappie succeeds, beacon keeps updating for 40+ ticks, guest stays up, ifconfig wlan0 destroy + kldunload fw complete cleanly.

fix_status: fixed, fix_baseline_reproduced: 1, fix_patched_reproduced: 0 (the bug no longer manifests).

Threat model notes

SIOCS80211 requires SYSCAP_NONET_WIFI (ieee80211_ioctl.c:3472) β€” root or a capability-granted network-wifi manager (hostapd/wpa_supplicant run as exactly this). The trigger ordering (beacon allocated before first app IE) is the common case for an AP whose config daemon attaches WPS/interworking IEs after the interface is already beaconing, and it is remotely inducible in WPS deployments (the WPS state machine rewrites beacon IEs on unauthenticated user interaction).

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

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched kernel rebuilt in-guest (make nativekernel + installkernel) with fix.diff applied: the exact baseline trigger sequence now completes without any fault (beacon ticks continue 40+, vap destroy + kldunload clean, guest up). Baseline on stock kernel panicked within one beacon tick.

Serial console before/after: panic.txt (baseline fault VA 0x40) vs run.log patched section + guest 'up' status after both triggers; kernel build log /root/kernelbuild2.log in guest.
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Thu Sep 10 04:41:20 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Exploit chain

hostapd/WPS-style beacon app-IE install on a running AP whose beacon predates the first app IE (SIOCS80211 IEEE80211_IOC_APPIE subtype BEACON, SYSCAP_NONET_WIFI) -> next TBTT ieee80211_beacon_update() -> NULL+adjust pointer arithmetic -> memcpy to 0x40 -> page fault -> kernel panic (DoS). Write address is confined to the unmapped zero page, so no escalation beyond panic.

Evidence (decisive lines)

['panic.txt (Fatal trap 12, fault VA 0x40, supervisor write, memcpy+0x1c)', 'run.log (5-step transcript)', 'fw.c (software ieee80211com with beacon_alloc/beacon_update callout)', 'setappie.c (SIOCS80211 APPIE trigger)', 'build.log (untrimmed KLD build)']

PoC changes

Wrote the entire harness (no seed existed): fw.c fake-wifi KLD (net80211 radio with hostap + C_TXFRAG + beacon callout), setappie.c; fixed ioctl calling convention (SIOCS80211 takes ieee80211req directly, not ifreq.ifr_data), DFly ifconfig clone syntax (wlanmode ap), added driver nop callbacks the state machine calls.

Verified recommended fix

In ieee80211_beacon_update()'s APPIE block, handle bo_appie==NULL by placing the first app IE in the space vacated at the head of the shifted trailer (and fold it into bo_tim_trailer/len so TIM resizes track it) instead of doing pointer arithmetic on NULL.

Verdict

ieee80211_beacon_update() performs 'bo->bo_appie += adjust' on a NULL bo->bo_appie when the first beacon app IE is installed after the beacon was constructed, then add_appie() memcpy's the IE to kernel address 0x0+ie_len: guaranteed fatal page fault. Reproduced on the stock INVARIANTS guest with a software net80211 radio (fw.ko) + privileged SIOCS80211 APPIE ioctl: 'Fatal trap 12, fault virtual address = 0x40 (= NULL + 64-byte IE), supervisor write, Stopped at memcpy+0x1c', guest down within 100ms of the ioctl. fix.diff (first-app-IE lands in the vacated trailer head) validated by kernel rebuild: ioctl succeeds, beacon updates continue, vap destroy + kldunload clean, guest up.