From 6c799b4998c87bb44574bb090b8728bb3b3dbc47 Mon Sep 17 00:00:00 2001 From: Cheng Yongkang Date: Thu, 22 Oct 2026 08:26:48 -0700 Subject: [PATCH] net: tun: hold napi_mutex when deleting NAPI in tun_detach_all() With IFF_NAPI_FRAGS, tun_get_user() takes tfile->napi_mutex, gets the skb from napi_get_frags() and copies the user data into it before it enters the RCU read-side section around napi_gro_frags(). tun_detach_all() waits with synchronize_net() and then calls tun_napi_del() without napi_mutex. netif_napi_del() frees napi->skb through napi_free_frags(). A writer that is still copying has not yet entered its RCU section, so synchronize_net() does not wait for it, and deleting the device frees the skb while the writer is using it: BUG: KASAN: slab-use-after-free in skb_copy_datagram_from_iter+0x175/0x720 Read of size 8 at addr ffff888077def990 by task br029-poc/9406 Call Trace: skb_copy_datagram_from_iter+0x175/0x720 tun_get_user+0xb42/0x3d00 tun_chr_write_iter+0x113/0x200 ... Freed by task 9405: kmem_cache_free+0x184/0x670 __netif_napi_del_locked+0x2bb/0x3c0 tun_net_uninit+0x566/0xa70 unregister_netdevice_many_notify+0x1a26/0x2090 rtnl_dellink+0x514/0x710 __tun_detach() also calls tun_napi_del() without the mutex, but only from tun_chr_close(), when no write on that file can be in flight. Take napi_mutex around tun_napi_del() in tun_detach_all() for both attached and disabled queues. Fixes: 90e33d459407 ("tun: enable napi_gro_frags() for TUN/TAP driver") Signed-off-by: Cheng Yongkang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/179268538303.70821.16548460883330758972@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/tun.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 439565a264ee..770b8fe085c3 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -688,14 +688,18 @@ static void tun_detach_all(struct net_device *dev) synchronize_net(); for (i = 0; i < n; i++) { tfile = rtnl_dereference(tun->tfiles[i]); + mutex_lock(&tfile->napi_mutex); tun_napi_del(tfile); + mutex_unlock(&tfile->napi_mutex); /* Drop read queue */ tun_queue_purge(tfile); xdp_rxq_info_unreg(&tfile->xdp_rxq); sock_put(&tfile->sk); } list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) { + mutex_lock(&tfile->napi_mutex); tun_napi_del(tfile); + mutex_unlock(&tfile->napi_mutex); tun_enable_queue(tfile); tun_queue_purge(tfile); xdp_rxq_info_unreg(&tfile->xdp_rxq);