[PATCH RFC v3] netdevsim: fix UAF by swapping teardown order

"syzbot" <[email protected]>
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
A slab-use-after-free was detected in debugfs_u32_get(). The issue arises
because the netdevsim device's debugfs files are removed after the
underlying memory for the netdevsim instance has been freed.

In __nsim_dev_port_del(), nsim_destroy() is called before
nsim_dev_port_debugfs_exit(). nsim_destroy() eventually calls
free_netdev(), which frees the struct netdevsim. However, the debugfs files
associated with this device still exist and can be accessed until
nsim_dev_port_debugfs_exit() is called, leading to a UAF.

Fix this by swapping the order of cleanup: remove the debugfs entries first
using nsim_dev_port_debugfs_exit(), then proceed with nsim_destroy(). This
is a simpler and more robust fix. Since nsim_dev_port_debugfs_exit()
performs a recursive removal of the port's debugfs directory, manual
removals of individual debugfs files in sub-modules are now redundant and
have been removed.

Also updated the error paths in nsim_create() and __nsim_dev_port_add() to
follow the same pattern.

BUG: KASAN: slab-use-after-free in debugfs_u32_get+0x6c/0x70
fs/debugfs/file.c:657
Read of size 4 at addr ffff888196b7db00 by task syz.2.2765/11946

Call Trace:
 <TASK>
 debugfs_u32_get+0x6c/0x70 fs/debugfs/file.c:657
 simple_attr_read+0x227/0x490 fs/libfs.c:1368
 debugfs_attr_read+0x76/0x130 fs/debugfs/file.c:523
 vfs_read+0x213/0xa80 fs/read_write.c:572
 ksys_read+0x150/0x270 fs/read_write.c:717
 do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 </TASK>

Freed by task 11226:
 kfree+0x1c5/0x640 mm/slub.c:6566
 device_release+0xc4/0x1f0 drivers/base/core.c:-1
 kobject_put+0x222/0x550 lib/kobject.c:737
 __nsim_dev_port_del+0x14e/0x200 drivers/net/netdevsim/dev.c:1547
 nsim_dev_reload_destroy+0x288/0x490 drivers/net/netdevsim/dev.c:1785

Fixes: e05b2d141fef ("netdevsim: move netdev creation/destruction to dev probe")
Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=6c25f4750230faf70be9
Link: https://syzkaller.appspot.com/ai_job?id=32031d0e-0992-44c9-8801-560fddd81e41
To: "Andrew Lunn" <[email protected]>
To: "David S. Miller" <[email protected]>
To: "Eric Dumazet" <[email protected]>
To: "Jakub Kicinski" <[email protected]>
To: <[email protected]>
To: "Paolo Abeni" <[email protected]>
Cc: <[email protected]>

---
v3:
- Swapped the order of nsim_dev_port_debugfs_exit() and nsim_destroy() in __nsim_dev_port_del() to ensure debugfs files are removed before the netdevsim structure is freed.
- Removed redundant debugfs cleanup calls in nsim_destroy(), nsim_ipsec_teardown(), nsim_psp_uninit(), and nsim_udp_tunnels_info_destroy() as they are now covered by the recursive removal of the port directory.
- Fixed debugfs cleanup in the error paths of nsim_create() and __nsim_dev_port_add().

v2:
- Switched to a symmetrical cleanup approach by explicitly removing debugfs entries in teardown functions instead of recursive removal of the port directory.
- Added nsim_ethtool_teardown() to remove the ethtool debugfs directory.
- Updated nsim_bpf_uninit() to remove BPF-related debugfs files using debugfs_lookup_and_remove().
- Called nsim_ethtool_teardown() in nsim_destroy() and the error path of nsim_create().
https://lore.kernel.org/all/[email protected]/T/

v1:
https://lore.kernel.org/all/[email protected]/T/
---
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index f00fc2f9e..beecc0c6e 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -1524,7 +1524,9 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ
 	return 0;
 
 err_nsim_destroy:
+	nsim_dev_port_debugfs_exit(nsim_dev_port);
 	nsim_destroy(nsim_dev_port->ns);
+	goto err_port_resource_unregister;
 err_port_debugfs_exit:
 	nsim_dev_port_debugfs_exit(nsim_dev_port);
 err_port_resource_unregister:
@@ -1544,8 +1546,8 @@ static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
 	list_del(&nsim_dev_port->list);
 	if (nsim_dev_port_is_vf(nsim_dev_port))
 		devl_rate_leaf_destroy(&nsim_dev_port->devlink_port);
-	nsim_destroy(nsim_dev_port->ns);
 	nsim_dev_port_debugfs_exit(nsim_dev_port);
+	nsim_destroy(nsim_dev_port->ns);
 	if (nsim_dev_port_is_pf(nsim_dev_port))
 		devl_port_resources_unregister(devlink_port);
 	devl_port_unregister(devlink_port);
diff --git a/drivers/net/netdevsim/ipsec.c b/drivers/net/netdevsim/ipsec.c
index 36a1be492..0bc9f42a5 100644
--- a/drivers/net/netdevsim/ipsec.c
+++ b/drivers/net/netdevsim/ipsec.c
@@ -292,5 +292,4 @@ void nsim_ipsec_teardown(struct netdevsim *ns)
 	if (ipsec->count)
 		netdev_err(ns->netdev, "tearing down IPsec offload with %d SAs left\n",
 			   ipsec->count);
-	debugfs_remove_recursive(ipsec->pfile);
 }
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index a75076891..6918679ca 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -1164,6 +1164,8 @@ struct netdevsim *nsim_create(struct nsim_dev *nsim_dev,
 	return ns;
 
 err_free_netdev:
+	debugfs_remove_recursive(nsim_dev_port->ddir);
+	nsim_dev_port->ddir = NULL;
 	free_netdev(dev);
 	return ERR_PTR(err);
 }
@@ -1174,10 +1176,6 @@ void nsim_destroy(struct netdevsim *ns)
 	struct netdevsim *peer;
 	u16 vid;
 
-	debugfs_remove(ns->vlan_dfs);
-	debugfs_remove(ns->qr_dfs);
-	debugfs_remove(ns->pp_dfs);
-
 	if (ns->nb.notifier_call)
 		unregister_netdevice_notifier_dev_net(ns->netdev, &ns->nb,
 						      &ns->nn);
diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c
index 6936ecb81..2f27a2114 100644
--- a/drivers/net/netdevsim/psp.c
+++ b/drivers/net/netdevsim/psp.c
@@ -227,7 +227,6 @@ static void __nsim_psp_uninit(struct netdevsim *ns, bool teardown)
 
 void nsim_psp_uninit(struct netdevsim *ns)
 {
-	debugfs_remove(ns->psp.rereg);
 	mutex_destroy(&ns->psp.rereg_lock);
 	__nsim_psp_uninit(ns, true);
 }
diff --git a/drivers/net/netdevsim/udp_tunnels.c b/drivers/net/netdevsim/udp_tunnels.c
index 89fff76e5..a69b15f12 100644
--- a/drivers/net/netdevsim/udp_tunnels.c
+++ b/drivers/net/netdevsim/udp_tunnels.c
@@ -188,9 +188,6 @@ int nsim_udp_tunnels_info_create(struct nsim_dev *nsim_dev,
 
 void nsim_udp_tunnels_info_destroy(struct net_device *dev)
 {
-	struct netdevsim *ns = netdev_priv(dev);
-
-	debugfs_remove_recursive(ns->udp_ports.ddir);
 	kfree(dev->udp_tunnel_nic_info);
 	dev->udp_tunnel_nic_info = NULL;
 }


base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
-- 
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).

See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.