Re: [PATCH net] net: usb: usbnet: fix use-after-free in race on workqueue
From: Peter GJ. Park
Date: Tue Jul 01 2025 - 00:19:27 EST
On Thu, Jun 26, 2025 at 11:21:39AM +0200, Paolo Abeni wrote:
> On 6/25/25 11:33 AM, Peter GJ. Park wrote:
> > When usbnet_disconnect() queued while usbnet_probe() processing,
> > it results to free_netdev before kevent gets to run on workqueue,
> > thus workqueue does assign_work() with referencing freeed memory address.
> >
> > For graceful disconnect and to prevent use-after-free of netdev pointer,
> > the fix adds canceling work and timer those are placed by usbnet_probe()
> >
> > Signed-off-by: Peter GJ. Park <gyujoon.park@xxxxxxxxxxx>
>
> You should include a suitable fixes tag, and you should have specified
> the target tree ('net' in this case) in the prefix subjext
>
It has been applied with v2 patch. Thank you for this.
> > ---
> > drivers/net/usb/usbnet.c | 3 +++
> > 1 file changed, 3 insertions(+)
> >
> > diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> > index c04e715a4c2ade3bc5587b0df71643a25cf88c55..3c5d9ba7fa6660273137c80106746103f84f5a37 100644
> > --- a/drivers/net/usb/usbnet.c
> > +++ b/drivers/net/usb/usbnet.c
> > @@ -1660,6 +1660,9 @@ void usbnet_disconnect (struct usb_interface *intf)
> > usb_free_urb(dev->interrupt);
> > kfree(dev->padding_pkt);
> >
> > + timer_delete_sync(&dev->delay);
> > + tasklet_kill(&dev->bh);
> > + cancel_work_sync(&dev->kevent);
> > free_netdev(net);
>
> This happens after unregister_netdev(), which calls usbnet_stop() that
> already performs the above cleanup. How the race is supposed to take place?
>
> /P
>
This uaf detected while my syzkaller project for usb kernel driver.
First hub_event work:usb_new_device arrived and processing in wq,
While second new hub_event work:usb_disconnect put into wq.
Then third usbnet:kevent work put by first hub_event work.
Dev pointer free-ed by second work without preceding usbnet_stop(), so the usbnetkevent still queued in wq,
Finally it detected by kasan as use-after-free when wq try to access that poiter.
Here is log snippet.
[ 3041.949116] [5: kworker/5:1: 130] BUG: KASAN: slab-use-after-free in assign_work+0xe8/0x280
[ 3041.949128] [5: kworker/5:1: 130] Read of size 8 at addr ffffff88ef60ece8 by task kworker/5:1/130
...
[ 3041.949155] [5: kworker/5:1: 130] Workqueue: 0x0 (usb_hub_wq)
[ 3041.949167] [5: kworker/5:1: 130] Call trace:
[ 3041.949170] [5: kworker/5:1: 130] dump_backtrace+0x120/0x170
[ 3041.949178] [5: kworker/5:1: 130] show_stack+0x2c/0x40
[ 3041.949184] [5: kworker/5:1: 130] dump_stack_lvl+0x68/0x84
[ 3041.949193] [5: kworker/5:1: 130] print_report+0x13c/0x6f8
[ 3041.949203] [5: kworker/5:1: 130] kasan_report+0xdc/0x13c
[ 3041.949211] [5: kworker/5:1: 130] __asan_load8+0x98/0xa0
[ 3041.949216] [5: kworker/5:1: 130] assign_work+0xe8/0x280
[ 3041.949224] [5: kworker/5:1: 130] worker_thread+0x458/0x670
[ 3041.949231] [5: kworker/5:1: 130] kthread+0x1d8/0x298
[ 3041.949238] [5: kworker/5:1: 130] ret_from_fork+0x10/0x20
[ 3041.949248] [5: kworker/5:1: 130] Allocated by task 130:
...
[ 3041.949275] [5: kworker/5:1: 130] __kmalloc_node+0x74/0x194
[ 3041.949282] [5: kworker/5:1: 130] kvmalloc_node+0x160/0x394
[ 3041.949289] [5: kworker/5:1: 130] alloc_netdev_mqs+0x6c/0x718
[ 3041.949298] [5: kworker/5:1: 130] alloc_etherdev_mqs+0x48/0x60
[ 3041.949305] [5: kworker/5:1: 130] usbnet_probe+0xd8/0xf10 [usbnet]
[ 3041.949348] [5: kworker/5:1: 130] usb_probe_interface+0x338/0x4fc
...
[ 3041.949516] [5: kworker/5:1: 130] usb_new_device+0x7c8/0xbdc
[ 3041.949523] [5: kworker/5:1: 130] hub_event+0x1808/0x2564
[ 3041.949530] [5: kworker/5:1: 130] process_scheduled_works+0x3cc/0x92c
[ 3041.949538] [5: kworker/5:1: 130] worker_thread+0x468/0x670
[ 3041.949546] [5: kworker/5:1: 130] kthread+0x1d8/0x298
[ 3041.949552] [5: kworker/5:1: 130] ret_from_fork+0x10/0x20
[ 3041.949561] [5: kworker/5:1: 130] Freed by task 130:
...
[ 3041.949604] [5: kworker/5:1: 130] __kmem_cache_free+0xa0/0x260
[ 3041.949610] [5: kworker/5:1: 130] kfree+0x60/0x124
[ 3041.949617] [5: kworker/5:1: 130] kvfree+0x40/0x54
[ 3041.949622] [5: kworker/5:1: 130] netdev_freemem+0x2c/0x40
[ 3041.949630] [5: kworker/5:1: 130] netdev_release+0x50/0x6c
[ 3041.949638] [5: kworker/5:1: 130] device_release+0x74/0x13c
[ 3041.949645] [5: kworker/5:1: 130] kobject_put+0xfc/0x1a8
[ 3041.949654] [5: kworker/5:1: 130] put_device+0x28/0x40
[ 3041.949660] [5: kworker/5:1: 130] free_netdev+0x234/0x284
[ 3041.949667] [5: kworker/5:1: 130] usbnet_disconnect+0x18c/0x250 [usbnet]
[ 3041.949706] [5: kworker/5:1: 130] usb_unbind_interface+0x130/0x414
[ 3041.949713] [5: kworker/5:1: 130] device_release_driver_internal+0x2e8/0x494
[ 3041.949721] [5: kworker/5:1: 130] device_release_driver+0x28/0x3c
[ 3041.949730] [5: kworker/5:1: 130] bus_remove_device+0x250/0x268
[ 3041.949737] [5: kworker/5:1: 130] device_del+0x304/0x530
[ 3041.949744] [5: kworker/5:1: 130] usb_disable_device+0x1d8/0x340
[ 3041.949750] [5: kworker/5:1: 130] usb_disconnect+0x1c4/0x4f0
[ 3041.949757] [5: kworker/5:1: 130] hub_event+0x1200/0x2564
[ 3041.949764] [5: kworker/5:1: 130] process_scheduled_works+0x3cc/0x92c
[ 3041.949772] [5: kworker/5:1: 130] worker_thread+0x468/0x670
[ 3041.949780] [5: kworker/5:1: 130] kthread+0x1d8/0x298
[ 3041.949786] [5: kworker/5:1: 130] ret_from_fork+0x10/0x20
[ 3041.949796] [5: kworker/5:1: 130] Last potentially related work creation:
...
[ 3041.949820] [5: kworker/5:1: 130] __queue_work+0x5d0/0xaf4
[ 3041.949827] [5: kworker/5:1: 130] queue_work_on+0x64/0xb0
[ 3041.949833] [5: kworker/5:1: 130] usbnet_link_change+0xdc/0x13c [usbnet]
[ 3041.949873] [5: kworker/5:1: 130] usbnet_probe+0xe5c/0xf10 [usbnet]
...
[ 3041.950078] [5: kworker/5:1: 130] usb_new_device+0x7c8/0xbdc
[ 3041.950085] [5: kworker/5:1: 130] hub_event+0x1808/0x2564
[ 3041.950092] [5: kworker/5:1: 130] process_scheduled_works+0x3cc/0x92c
[ 3041.950100] [5: kworker/5:1: 130] worker_thread+0x468/0x670
[ 3041.950107] [5: kworker/5:1: 130] kthread+0x1d8/0x298
[ 3041.950113] [5: kworker/5:1: 130] ret_from_fork+0x10/0x20
Best Regards,
GJ