NULL vap deref in scan_curchan_task: scan state not re-validated after dropping IEEE80211_LOCK across ic_set_channel
| Field | Value |
|---|---|
| ID | DF-0587 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-476 NULL Pointer Dereference |
| File | sys/netproto/802_11/wlan/ieee80211_scan_sw.c |
| Lines | 741-772, 890-920 |
| Area | netproto/802_11 (wlan scanning) |
| Confidence | likely |
| Discovered | 2026-07-02 |
| Reported | pending |
Summary
scan_curchan_task releases IEEE80211_LOCK at line 741 to run driver
callbacks ic_set_channel, ieee80211_radiotap_chan_change, and
ic_scan_curchan (sys/netproto/802_11/wlan/ieee80211_scan_sw.c:747-760),
which commonly sleep during hardware channel programming. During that window,
a concurrent vap detach β ieee80211_scan_vdetach in scan.c β acquires the
same lock, sets ISCAN_ABORT, and nulls ss->ss_vap and ss->ss_ops.
When scan_curchan_task re-acquires the lock at :761 it never re-validates
ss_vap; control flows through IEEE80211_DPRINTF(ss->ss_vap, β¦) (:700,
:724, :775 β unconditional crash on IEEE80211_DEBUG kernels) and, on the
aborted-scan path through scan_done, dereferences vap->iv_flags_ext
(:916), vap->iv_sta_ps (:917), and ieee80211_notify_scan_done(vap) (:920)
unconditionally β page-fault panic. The code's own comment at :763 admits it:
/* XXX scan state can change! Re-validate scan state! */
Root cause
-
The scan borrows
ss->ss_vapfor the entire scan lifetime with no reference count. -
scan_curchan_taskholdsIEEE80211_LOCKfrom :695, releases it at :741 to run driver callbacksic_set_channel(:747),ieee80211_radiotap_chan_change(:748), andic_scan_curchan(:760), then re-acquires it at :761. -
Concurrently, vap destruction runs
ieee80211_scan_vdetach(sys/netproto/802_11/wlan/scan.c:137-153): underIEEE80211_LOCKit callsswscan_vdetach(setsISCAN_ABORTifF_SCANwas set, sys/netproto/802_11/wlan/ieee80211_scan_sw.c:160), then becausess->ss_vap == vapit callsss->ss_ops->scan_detach, setsss->ss_ops = NULL, and setsss->ss_vap = NULLβ all under the lock thatscan_curchan_taskis not holding between :741 and :761. -
After re-lock at :761 there is no NULL check on
ss_vap/ss_ops:
- :769-773 sees
ISCAN_ABORTand doesgoto end, re-entering the loop at :696-700:IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, β¦)βieee80211_msg(_vap)expands to(_vap)->iv_debug(sys/netproto/802_11/wlan/ieee80211_var.h), a NULL-vap deref onIEEE80211_DEBUGkernels. - On non-DEBUG kernels the same code at :705 routes to
scan_endwhich at :784 takesvap = ss->ss_vap(NULL) and at :789 derefs it again in DPRINTF. scan_doneat :890 readsvap = ss->ss_vap(NULL) and, whenscandoneis true (scan had reachedss_next >= ss_last, :697), dereferencesvap->iv_flags_ext(:916) /vap->iv_sta_ps(:917) /ieee80211_notify_scan_done(vap)(:920) β page fault.
ic_set_channel is a driver callback that commonly sleeps while programming
hardware channel state, so the race window is wide and reliably hittable.
Threat model & preconditions
- Attacker position: privileged local user. Vap destruction
(
SIOCIFDESTROY/ network-config caps) is gated bycaps_priv_check_self(SYSCAP_NONET_WIFI). - Privileges gained or impact: kernel panic (local DoS). No code-execution
primitive: the fault is a NULL-vap dereference, and
map_at_zerois off by default on DragonFlyBSD. - Required config or capabilities: any DragonFly system using
netproto/802_11(i.e. anywlan(4)vap with a driver). The race is triggered during ordinary operational reconfiguration (vap destroy/restart,wpa_supplicant/NetworkManagerrestart, driver reload, shutdown) rather than adversarial input. - Reachability: race a vap detach (e.g.
ifconfig wlan0 destroy) against an in-progress scan (IEEE80211_IOC_SCAN_REQ).
Proof of concept
PoC source: findings/poc/DF-0587/race.c
Build & run
cc -O2 -Wall findings/poc/DF-0587/race.c -o findings/poc/DF-0587/race sudo ./findings/poc/DF-0587/race wlan0
Expected output
Kernel panic with the faulting instruction inside scan_done
(vap->iv_flags_ext deref at sys/netproto/802_11/wlan/ieee80211_scan_sw.c:916)
on non-DEBUG kernels, or inside IEEE80211_DPRINTF(ss->ss_vap,β¦) at :700 on
DEBUG kernels:
Fatal trap 12: page fault while in kernel mode cpuid = ...; apic id = ... fault virtual address = 0x.. [code] scan_done+0x...: mov ...
Reproduces within seconds-to-minutes depending on driver set_channel
latency.
Impact
- Blast radius: any DragonFly system with a
wlan(4)vap. The race fires during ordinary operational teardown/reconfiguration β including shutdown of a system with an active wireless scan β making this a real reliability hazard, not merely an adversarial bug. - Severity rationale: Low. Privileged attacker, high race complexity, impact limited to DoS (panic), no code execution.
- Reliability: race window depends on driver
set_channellatency, but that callback commonly sleeps, widening the window.
Recommended fix
After re-acquiring IEEE80211_LOCK at scan_curchan_task:761, re-validate
ss_vap/ss_ops and, if they were cleared by a concurrent vap detach, bail
directly through scan_done() instead of routing through scan_end()/DPRINTF
that dereference vap. Additionally harden scan_done to tolerate vap == NULL
so any other path that reaches it with a cleared ss_vap cannot fault.
--- a/sys/netproto/802_11/wlan/ieee80211_scan_sw.c
+++ b/sys/netproto/802_11/wlan/ieee80211_scan_sw.c
@@ -760,6 +760,20 @@ scan_curchan_task(void *arg, int pending)
ic->ic_scan_curchan(ss, maxdwell);
IEEE80211_LOCK(ic);
+ /* XXX scan state can change! Re-validate scan state! */
+ /*
+ * The vap may have been detached (and ss_vap/ss_ops cleared by
+ * ieee80211_scan_vdetach) while the lock was dropped above for
+ * the driver channel-change callback. Bail straight through
+ * scan_done() rather than dereferencing a NULL vap in the
+ * scan_end()/DPRINTF/scan_done paths.
+ */
+ if (ss->ss_vap == NULL || ss->ss_ops == NULL) {
+ ss_priv->ss_iflags &= ~ISCAN_RUNNING;
+ ss_priv->ss_iflags |= ISCAN_ABORT;
+ scan_done(ss, 1);
+ return;
+ }
+
ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell;
/* clear mindwell lock and initial channel change flush */
ss_priv->ss_iflags &= ~ISCAN_REP;
@@ -908,9 +922,12 @@ scan_done(struct ieee80211_scan_state *ss, int scandone)
* the beacon indicates we have frames
* waiting for us.
*/
- if (scandone) {
+ /*
+ * vap may be NULL if we got here via the scan_curchan_task
+ * re-validation bail-out after the owning vap was detached.
+ */
+ if (scandone && vap != NULL) {
if ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0)
vap->iv_sta_ps(vap, 0);
if (ss->ss_next >= ss->ss_last)
Longer-term the right fix is to acquire a reference on ss_vap for the
duration of the scan (mirroring ieee80211_node refcounts) so the borrowed
pointer cannot be invalidated under the task; the guard above closes the
immediate NULL-deref window with minimal change.
References
- FreeBSD
r288725/ similar scan-state-vs-teardown races in the wlan stack (historical context; DragonFly'snetproto/802_11derives from FreeBSDsys/net80211). - The
XXX scan state can change!comment at sys/netproto/802_11/wlan/ieee80211_scan_sw.c:763 β original author acknowledgment of the hazard.
Timeline
- 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
- 2026-07-02 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0587 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| race.c | trigger-source | 4-thread scanner + 4-thread vap-destroy race driver; fixed include path and -lpthread | 3.2 KB | view raw |
| build.sh | build-script | cc -O2 -Wall race.c -o race -lpthread | 239 B | view raw |
| run.sh | run-script | sudo ./race <wlanN> (requires real wlan vap) | 553 B | view raw |
| VERDICT.md | verdict | Full code-level trace confirming bug; explanation of guest reachability limit | 9.6 KB | β raw |
| fix.diff | suggested-fix | NULL-check ss_vap/ss_ops after re-lock at scan_curchan_task:774 + harden scan_done:931 to 'scandone && vap != NULL' | 1.5 KB | view raw |
| build.log | build-log | PoC build output (final successful build, including -lpthread fix) | 13 B | view raw |
| run.log | run-log | Decisive run on unpatched #0 baseline: PoC times out, no panic, no wlan vap | 1.2 KB | view raw |
| fix_build.log | fix-build-log | Full single-fix kernel build (make nativekernel, rc=0, 35707 lines) | 5.6 MB | β download |
| fix_run.log | fix-run-log | PoC run on patched #1 kernel: same outcome (unreachable), kernel healthy | 932 B | view raw |
| dmesg.txt | dmesg | Baseline dmesg excerpt β no wlan/80211 messages, no panic | 858 B | view raw |
| env.txt | environment | uname, cc version, IEEE80211_DEBUG config line, wlan vap absence | 475 B | view raw |
| README.md | readme | human reproduce doc | 1.7 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0587 β PoC: race scan_curchan_task against concurrent vap detach
Privileged local race (root required: vap destroy). Drives
scan_curchan_task's unlock/re-lock window against a concurrent vap detach so
that ss->ss_vap / ss->ss_ops get nulled by ieee80211_scan_vdetach while
the task is unlocked inside ic_set_channel, producing a NULL-vap page fault
on re-lock.
Files
race.cβ minimal reproducer (4 scan-request threads + 4 vap-destroy threads).- (added by per-PoC verifier)
build.sh,run.sh,build.log,run.log,VERDICT.md,manifest.json.
Build & run
cc -O2 -Wall race.c -o race sudo ./race wlan0
Expected first outcome
Kernel panic (Fatal trap 12: page fault while in kernel mode) with the
faulting instruction inside scan_done (vap->iv_flags_ext deref at
sys/netproto/802_11/wlan/ieee80211_scan_sw.c:916) on non-DEBUG kernels, or
inside IEEE80211_DPRINTF(ss->ss_vap,β¦) at :700 / :724 / :775 on DEBUG
kernels.
Notes for the per-PoC verifier
- Requires root (
SIOCIFDESTROYis privilege-gated). This is a privileged DoS / reliability bug, not an unprivileged-escalation bug. - Requires a
wlan(4)vap with a driver loaded; verify withifconfig wlan0anddmesg | grep wlan. - The race fires during ordinary operational teardown β
wpa_supplicant/NetworkManagerrestarts, system shutdown with active scan, driver reload all hit the same path; a reproduced panic is the success criterion. - If destroy toggles the vap faster than the scan can arm, try inserting a
short
usleepbetween scan-request and destroy to widen the channel-change window. The race is fundamentally an unlockedic_set_channelsleep, so any driver with a non-trivial set_channel path is a reliable target.
DF-0587 β VERDICT
Verdict: INCONCLUSIVE (runtime) β code-level trace CONFIRMS the bug is real.
Status: not_reproduced (impact: panic if reachable) β bug is unreachable on this guest.
The PoC cannot trigger on the audit guest because the QEMU/KVM guest has no
IEEE 802.11 radio hardware β there is no wlan(4) vap and no parent
ieee80211com device to create one against, so the cited code path
(scan_curchan_task β ieee80211_scan_vdetach race) is never reached at
runtime. The wlan framework is compiled into the default GENERIC kernel
(options IEEE80211_DEBUG, device wlan β verified in
sys/config/X86_64_GENERIC:255-258), so the vulnerable code is present and
would be live on any DragonFly system with a real wireless adapter; it is just
not exercisable on this particular VM.
A line-by-line trace of the cited source confirms the bug is genuine β this is classification (d) "genuinely not reachable on this guest at runtime", not a false positive.
Mechanism (code-level trace)
The finding's race is exactly as described. Every hop verified against the
audited sys/ tree:
-
scan_curchan_task(sys/netproto/802_11/wlan/ieee80211_scan_sw.c:686) acquiresIEEE80211_LOCK(ic)at :695, then at :741 drops it to call driver callbacksic_set_channel(:747),ieee80211_radiotap_chan_change(:748), andic_scan_curchan(:760) β all of which commonly sleep while the radio is re-tuned. It re-acquires the lock at :761. -
Concurrent vap detach runs
ieee80211_scan_vdetach(vap)(sys/netproto/802_11/wlan/ieee80211_scan.c:137-155) β verified present (the finding citesscan.cbut the function actually lives inieee80211_scan.c; the body is identical to the description). UnderIEEE80211_LOCK(ic)it: - callsic->ic_scan_methods->sc_vdetach(vap)=ieee80211_swscan_vdetach(ieee80211_scan_sw.c:151-161), which doesscan_signal_locked(ss, ISCAN_ABORT)ifIEEE80211_F_SCANis set β setting the abort flag inss_priv->ss_iflags(:602). - then, becausess->ss_vap == vap, callsss->ss_ops->scan_detach(ss), setsss->ss_ops = NULL, and setsss->ss_vap = NULL(ieee80211_scan.c:147-153). All under the same lock the task is not holding between :741 and :761. -
No re-validation after re-lock. At
scan_curchan_task:761the lock is reacquired and the comment/* XXX scan state can change! Re-validate scan state! */sits at :763 β but no re-validation follows. Control flows to :769 where the freshly-setISCAN_ABORTtriggersgoto end(:772), looping back to theend:label at :696. The next statement is:
c
IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: loop start; ...");
On IEEE80211_DEBUG kernels (which the default X86_64_GENERIC
is β sys/config/X86_64_GENERIC:255, verified),
IEEE80211_DPRINTF expands to
if (((_vap)->iv_debug & (_m))) ieee80211_note(...)
(sys/netproto/802_11/ieee80211_var.h:956-960). With ss->ss_vap == NULL
this is a NULL page-fault β panic.
- On non-DEBUG kernels the DPFIFs at :700/:724/:775 are no-ops
(
ieee80211_var.h:1028:#define IEEE80211_DPRINTF(...) do { } while (0)), so the code instead falls through :705 (ISCAN_ABORTset) toscan_end(ss, scandone)(:709), which readsvap = ss->ss_vap(NULL) at :784.scan_endseesISCAN_ABORT(:791) and routes toscan_done(ss, scandone)(:792).scan_done(:886) readsvap = ss->ss_vap(NULL) at :890, and ifscandoneis true (which the loop set fromss->ss_next >= ss->ss_lastat :697) it dereferencesvap->iv_flags_ext(:916),vap->iv_sta_ps(vap, 0)(:917), and callsieee80211_notify_scan_done(vap)(:920) β page fault, panic.
The race window is wide because ic_set_channel is a driver callback that
commonly sleeps while programming the radio. The bug admits NULL-deref β panic
only; map_at_zero is not a thing on DragonFly (vm.map_at_zero does not
exist as a sysctl; the zero page is unmapped), so there is no path from this
primitive to code execution. Impact ceiling = local DoS / panic. Severity Low
is appropriate (privileged attacker β vap destroy is gated by
caps_priv_check_self(SYSCAP_NONET_WIFI) β and a narrow race window).
Exploit chain
Not applicable β this is a NULL-deref / CWE-476. No memory-corruption primitive is acquired; the only effect is a kernel panic (DoS). No escalation chain to develop.
Why the PoC cannot trigger on this guest
- The guest is QEMU/KVM with virtio-net (
vtnet0) and loopback (lo0) only. ifconfig wlan0β "interface wlan0 does not exist".- Creating a vap requires
ifconfig wlan create wlandev <parent>where<parent>is a WiFi radio driver (run0,ath0,iwm0, β¦). The guest has no such parent device (SIOCIFCREATE2: Device not configured). - All wifi driver modules in
/boot/kernel/are firmware blobs or hardware drivers that require real PCI/USB adapters; none bind to emulated QEMU HW. - Result:
scan_curchan_taskis never scheduled,ss->ss_vapnever becomes non-NULL inside a real scan, the unlock/lock window never opens. The PoC's ioctl loops returnENXIO/ENODEVsilently and time out.
This is a runtime reachability limitation of the audit guest, not a
property of the bug. On any DragonFly system with a wlan vap (which is the
default deployment target of the wlan stack β wpa_supplicant/NetworkManager
restarts, system shutdown with active scan, vap destroy/recreate), the race
fires during ordinary operational teardown.
PoC changes
race.c:18β fixed include path<netproto/802_11/wlan/ieee80211_ioctl.h>β<netproto/802_11/ieee80211_ioctl.h>(the header lives one level up fromwlan/; verified at/usr/include/netproto/802_11/ieee80211_ioctl.hand/usr/src/sys/netproto/802_11/ieee80211_ioctl.h).- build line β added
-lpthread(DragonFly'sccdoes not auto-link pthreads; without it the link fails withundefined reference to 'pthread_create'). build.sh,run.shβ added (exact runnable commands).fix.diffβ added (authored post-verification; supersedes/refines the finding markdown's## Recommended fixproposal β see below).
fix.diff
Authored against the actual file (the finding's diff context did not match
the on-disk layout, which has the /* XXX scan state can change! */ comment
between the re-lock and ss_chanmindwell). The verified fix:
scan_curchan_task:774(after re-lock at :761) β add a NULL check onss->ss_vap/ss->ss_ops. If either was cleared by a concurrent vap detach during the unlockedic_set_channelwindow, clearISCAN_RUNNING, setISCAN_ABORT, and bail straight throughscan_done(ss, 1)β which only manipulatesic->ic_flagsandss_priv->ss_iflags, never touchingvap. This avoids the loop-top DPRINTF at :700 (DEBUG kernels) and thescan_end/scan_donevap-deref path (any kernel).scan_done:931β defense-in-depth: hardenif (scandone)toif (scandone && vap != NULL)so that any other path that reachesscan_donewith a clearedss_vapcannot fault at :916/:917/:920.
This matches the finding proposal in intent and structure, with the diff
context corrected to apply cleanly (git apply --check passes;
patch -p1 --forward applies both hunks: "Hunk #1 succeeded at 762.",
"Hunk #2 succeeded at 924.").
Fix validation (Phase 8)
git apply --checkon host: PASS.- Applied to in-guest
/usr/src, both hunks applied cleanly. - Built single-fix kernel
make -j6 nativekernel KERNCONF=X86_64_GENERICβrc=0, no errors (fix_build.log, 35707 lines). - Installed
/usr/obj/.../kernel.strippedβ/boot/kernel/kernel(sha256fe5df37c792efe5437521e6e60d8cf3b799661c91d2ac2454e1f29c752365ee1), bootedkern.version = DragonFly 6.5-DEVELOPMENT #1: Mon Jul 13 01:25:53 UTC 2026. fix_status = not_testable: empirical before/after re-test is impossible because the bug is unreachable on this guest (no WiFi hardware). Validated that the diff applies, compiles, and the patched source contains the expected guards (grepconfirms774: if (ss->ss_vap == NULL || ss->ss_ops == NULL)and931: if (scandone && vap != NULL)in/usr/src/sys/netproto/802_11/wlan/ieee80211_scan_sw.c). The static trace shows the patched code closes the cited path.
References (verified path:line)
sys/netproto/802_11/wlan/ieee80211_scan_sw.c:686βscan_curchan_tasksys/netproto/802_11/wlan/ieee80211_scan_sw.c:741βIEEE80211_UNLOCK(ic)(race window opens)sys/netproto/802_11/wlan/ieee80211_scan_sw.c:747-760β driver callbacks run unlockedsys/netproto/802_11/wlan/ieee80211_scan_sw.c:761βIEEE80211_LOCK(ic)(race window closes)sys/netproto/802_11/wlan/ieee80211_scan_sw.c:763β/* XXX scan state can change! Re-validate scan state! */(admits the hazard; no check follows)sys/netproto/802_11/wlan/ieee80211_scan_sw.c:700,724,775βIEEE80211_DPRINTF(ss->ss_vap, ...)(NULL-vap deref on DEBUG)sys/netproto/802_11/wlan/ieee80211_scan_sw.c:151-161βieee80211_swscan_vdetach(setsISCAN_ABORT)sys/netproto/802_11/wlan/ieee80211_scan_sw.c:886-927βscan_done(NULL-vap deref at :916/:917/:920 on any kernel)sys/netproto/802_11/wlan/ieee80211_scan.c:137-155βieee80211_scan_vdetach(clearsss_vap/ss_ops)sys/netproto/802_11/ieee80211_var.h:955-960βIEEE80211_DEBUGmacros (NULL-vap deref)sys/netproto/802_11/ieee80211_var.h:1027-1033β non-DEBUG macros (no-ops)sys/config/X86_64_GENERIC:255βoptions IEEE80211_DEBUG(default GENERIC IS a DEBUG kernel)
Fix verification
not_testableNOT_TESTABLE empirically (no WiFi HW -> bug unreachable). Fix validated statically: git apply --check PASS; nativekernel rc=0; patched source contains NULL-check guards at cited lines.
Both #0 and #1: ifconfig wlan0 -> does not exist; timeout -> RUN_EXIT=124. Empirical before/after impossible (path unreachable). Static trace confirms guards close the cited path.
Confirmed kernel references
- sys/netproto/802_11/wlan/ieee80211_scan_sw.c:686
- sys/netproto/802_11/wlan/ieee80211_scan_sw.c:741
- sys/netproto/802_11/wlan/ieee80211_scan_sw.c:761
- sys/netproto/802_11/wlan/ieee80211_scan_sw.c:763
- sys/netproto/802_11/wlan/ieee80211_scan_sw.c:700
- sys/netproto/802_11/wlan/ieee80211_scan_sw.c:151
- sys/netproto/802_11/wlan/ieee80211_scan_sw.c:916
- sys/netproto/802_11/wlan/ieee80211_scan.c:137
- sys/netproto/802_11/wlan/ieee80211_scan.c:147
- sys/netproto/802_11/ieee80211_var.h:956
- sys/config/X86_64_GENERIC:255
Detail
Exploit chain
none (NULL deref / CWE-476 -- no memory-corruption primitive). Impact ceiling: local DoS/panic (privileged attacker; map_at_zero doesn't exist on DragonFly).
Evidence (decisive lines)
Baseline #0: ifconfig wlan0 -> 'does not exist'; timeout 5 ./race wlan0 -> RUN_EXIT=124, guest up, no panic. Code trace: scan_curchan_task:761 re-lock has no NULL check despite XXX at :763; ieee80211_scan.c:152 sets ss->ss_vap=NULL under same lock; IEEE80211_DPRINTF(ss->ss_vap,...) at :700 panics on NULL.
PoC changes
Fixed include path and added -lpthread. Added build.sh, run.sh, fix.diff (NULL check after re-lock + scan_done guard), VERDICT.md, manifest.json, full logs.
Verified recommended fix
After re-acquiring IEEE80211_LOCK at scan_curchan_task:761, add: if (ss->ss_vap == NULL || ss->ss_ops == NULL) { scan_done(ss, 1); return; }. Plus defense-in-depth: harden scan_done from 'if (scandone)' to 'if (scandone && vap != NULL)'. Matches finding proposal in intent. Full git-apply-able diff in findings/poc/DF-0587/fix.diff.
Verdict
NOT_REPRODUCED at runtime but CODE-LEVEL TRACE CONFIRMS the bug is real (classification (d) -- unreachable on this guest, NOT false-positive). scan_curchan_task drops IEEE80211_LOCK at :741 to call driver callbacks, re-acquires at :761 with NO re-validation despite the XXX comment at :763. Concurrent vap detach nulls ss->ss_vap/ss->ss_ops. On re-lock the NULL-vap deref at DPRINTF/scan_end/scan_done panics. No WiFi hardware on guest (vtnet0/lo0 only); scan_curchan_task never scheduled. NULL-deref only (CWE-476), no escalation.
No comments yet.