Synopsis: urtw(4) panics with fatal page fault on "ifconfig urtw0 up" (riscv64)
Category: kernel
Environment:
System : OpenBSD 7.9
Details : OpenBSD 7.9 (GENERIC.MP) #5: Wed May 6 13:53:42 MDT 2026
Architecture: OpenBSD.riscv64
Machine : riscv64 (StarFive VisionFive 2)
Description:
Bringing up a urtw(4) interface (Realtek RTL8187, rev 0x04, RFv2)
with no prior configuration (no nwid/wpakey set) panics the kernel
with a fatal page fault, triggered by the very first INIT -> SCAN
802.11 state transition.
Root cause:
ic->ic_bss->ni_chan is initialized to IEEE80211_CHAN_ANYC by
ieee80211_node_attach() (sys/net80211/ieee80211_node.c). This macro
is literally defined as ((struct ieee80211_channel *) NULL)
(sys/net80211/ieee80211_var.h).
urtw_newstate() (sys/dev/usb/if_urtw.c) only records the requested
state/arg and defers to urtw_task() via usb_add_task(); it never
assigns a real channel to ic->ic_bss->ni_chan before the generic
state machine runs. urtw_task() itself handles the NULL/ANYC value
gracefully in its own urtw_set_chan() call (ieee80211_chan2ieee()
returns IEEE80211_CHAN_ANY for a NULL/ANYC channel, and
urtw_set_chan() returns early on that), but it then calls
sc->sc_newstate() -- the original generic ieee80211_newstate() --
which does not tolerate this.
Inside ieee80211_newstate()'s IEEE80211_S_SCAN case,
ieee80211_node_abg_mode(ic, ni) is called with ni->ni_chan still
NULL. That function evaluates IEEE80211_IS_CHAN_5GHZ(ni->ni_chan),
which expands to ((ni->ni_chan)->ic_flags & IEEE80211_CHAN_5GHZ),
dereferencing ic_flags -- the *second* field (u_int16_t) of
struct ieee80211_channel -- with no NULL/ANYC check. This is
exactly consistent with the crash data below: stval == 0x2 is
precisely the byte offset of ic_flags within the struct when read
through a NULL base pointer.
Other drivers apparently avoid this by assigning a concrete
channel (typically ic->ic_ibss_chan, already computed by
ieee80211_setmode() during ieee80211_ifattach()) to
ic->ic_bss->ni_chan before or during their newstate handling.
urtw(4) never does this.
How-To-Repeat:
On riscv64 with a urtw(4)-attached RTL8187 adapter and no prior
/etc/hostname.urtw0 configuration:
# ifconfig urtw0 up
This immediately panics.
Crash data (ddb):
sepc == 0xffffffc00053365e
stval == 0x0000000000000002
scause == 0x000000000000000d (load page fault)
panic: Fatal page fault at 0xffffffc00053365e: 0x000002
panic() at panic+0xfc
do_trap_supervisor() at do_trap_supervisor+0x1f4
cpu_exception_handler_supervisor() at
cpu_exception_handler_supervisor+0x7a
ieee80211_newstate() at ieee80211_newstate+0x2e6
urtw_task() at urtw_task+0x216
usb_task_thread() at usb_task_thread+0xe0
proc_trampoline() at proc_trampoline+0xc
Fix:
Give ic->ic_bss->ni_chan a real channel in urtw_newstate() before
deferring to the generic state machine, mirroring what other
drivers do. Tested on the hardware above: urtw0 now brings up and
scans without panicking.
Index: sys/dev/usb/if_urtw.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_urtw.c,v
retrieving revision 1.74
diff -u -p -r1.74 if_urtw.c
--- sys/dev/usb/if_urtw.c
+++ sys/dev/usb/if_urtw.c
@@ -1018,6 +1018,16 @@ urtw_newstate(struct ieee80211com *ic, e
usb_rem_task(sc->sc_udev, &sc->sc_task);
timeout_del(&sc->scan_to);
+ /*
+ * ic->ic_bss->ni_chan starts out as IEEE80211_CHAN_ANYC (NULL).
+ * Give it a real channel before the generic 802.11 state machine,
+ * invoked below via sc->sc_newstate() in urtw_task(), dereferences
+ * ni_chan on the first INIT -> SCAN transition and panics.
+ */
+ if (nstate == IEEE80211_S_SCAN &&
+ ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC)
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;