CVE-2026-80947: wifi: rtl8xxxu: fix use-after-free from rx_urb_wq on stop
In the Linux kernel, the following vulnerability has been resolved:
wifi: rtl8xxxu: fix use-after-free from rxurbwq on stop
rtl8xxxu arms rxurbwq from the RX completion path: rtl8xxxurxcomplete() hands the URB to rtl8xxxuqueuerxurb(), which queues it on rxurbpendinglist and, once the list grows past RTL8XXXURXURBPENDINGWATER, schedules rxurbwq. The worker rtl8xxxurxurbwork() drains rxurbpendinglist, recovers priv through containerof, and resubmits each URB through rtl8xxxusubmitrxurb(), which anchors it on rxanchor and dereferences priv->udev.
rtl8xxxustop() cancels the sibling work items (c2hcmdwork, rawatchdog, updatebeaconwork) but never cancels rxurbwq, so a worker armed during the last burst of RX traffic can run rtl8xxxurxurbwork() after rtl8xxxudisconnect() has called ieee80211freehw(), which frees priv, producing a use-after-free. The window opens under active RX traffic (pending count above the watermark) followed by a disconnect.
There are two teardown races to close:
rtl8xxxuqueuerxurb() decided whether to enqueue under rxurblock but called schedulework() after dropping the lock. A completion that observed shutdown == false and released the lock could then call schedulework() after rtl8xxxustop() had set shutdown and cancelworksync() had already returned, arming the worker to run after the teardown. Move schedulework() under the same !shutdown branch so the arming decision is atomic with the shutdown check.
rtl8xxxurxurbwork() anchors every URB it drained back onto rxanchor through rtl8xxxusubmitrxurb(). A worker still running when usbkillanchoredurbs(&priv->rxanchor) returned would submit a URB that escaped the kill. In rtl8xxxustop(), call cancelworksync(&priv->rxurbwq) before the kill so the worker is drained first.
After priv->shutdown is set under rxurblock, completions can no longer queue rxurbwq. cancelworksync() then drains the last queued or running worker, and the following usbkillanchoredurbs() kills the URBs it may have submitted.
rtl8xxxudisconnect() is covered because ieee80211unregisterhw() guarantees .stop() runs for a live interface before ieee80211freehw() frees priv. The probe error path needs no cancel: rxurbwq is INITWORK()'d there but cannot have been scheduled, since no URB is submitted before ieee80211registerhw() succeeds.
This bug was found by static analysis.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Configuration
Change rtl8xxxu so schedule_work() for rx_urb_wq is done only under the same !shutdown condition used with rx_urb_lock (so the worker cannot be armed after priv->shutdown is set).
rtl8xxxu (rx_urb_wq scheduling) schedule_work() arming decision placement = Move schedule_work() under the same !shutdown check (atomic with shutdown) - Configuration
In rtl8xxxu_stop(), invoke cancel_work_sync(&priv->rx_urb_wq) before killing anchored URBs, to prevent the stop path from racing with rx_urb_wq restarting/using priv after cancel_work_sync() has already returned.
rtl8xxxu (rx_urb_wq teardown) cancel_work_sync() ordering relative to URB kill = Call cancel_work_sync(&priv->rx_urb_wq) before usb_kill_anchored_urbs(&priv->rx_anchor) - Compensating control
Ensure teardown ordering prevents rx_urb_wq from running after ieee80211_free_hw(priv) by canceling rx_urb_wq work (cancel_work_sync) during rtl8xxxu_stop before freeing priv via ieee80211_free_hw()/interface teardown.
Event History
Frequently Asked Questions
Which deployments are exposed to this teardown race?
Linux systems using the rtl8xxxu Wi-Fi driver are exposed when receive traffic is active enough to grow the pending RX URB list past its watermark and the device is then disconnected.
What occurs during the vulnerable shutdown sequence?
The RX worker can remain scheduled after disconnect has freed the driver's private state. When it runs, it may resubmit RX URBs while dereferencing the freed private structure and its USB device pointer.