debugfs files created under a port's ddir (ethtool/get_err, ethtool/set_err, ring params, bpf_offloaded_id, udp_ports/inject_error, etc.) store raw pointers directly into the netdevsim struct, which lives in the net_device private data kmalloc slab. If these files outlive the netdevsim struct, a concurrent reader can trigger a slab-use-after-free by passing debugfs_file_get() (which only checks dentry lifetime) and then dereferencing the freed data pointer in debugfs_u32_get(). In __nsim_dev_port_del(), nsim_destroy() is called before nsim_dev_port_debugfs_exit(). However, nsim_destroy() calls free_netdev() at its end, while nsim_dev_port_debugfs_exit() removes the port's debugfs directory. This means the slab is freed before the debugfs files are removed. The same window exists on nsim_create()'s error path: nsim_ethtool_init() creates debugfs files under ddir with pointers into ns before nsim_init_netdevsim()/nsim_init_netdevsim_vf() which can fail, and the err_free_netdev label calls free_netdev() while those debugfs entries are still live. Fix both paths by calling debugfs_remove_recursive() on the port's ddir before every free_netdev() call. The subsequent nsim_dev_port_debugfs_exit() calls become harmless no-ops since ddir is set to NULL. Reported-by: syzbot+6c25f4750230faf70be9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=6c25f4750230faf70be9 Fixes: e05b2d141fef ("netdevsim: move netdev creation/destruction to dev probe") Signed-off-by: Hrushiraj Gandhi --- v2: - Also fix the same use-after-free window on the error path of nsim_create() as suggested by Simon Horman. - Shorten the code comment in nsim_destroy() to be more concise. drivers/net/netdevsim/netdev.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 27e5f109f933..f2824e75cddd 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -1165,6 +1165,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); } @@ -1214,6 +1216,13 @@ void nsim_destroy(struct netdevsim *ns) ns->page = NULL; } + /* + * Remove per-port debugfs files before free_netdev() releases the + * netdevsim struct to prevent use-after-free in concurrent readers. + */ + debugfs_remove_recursive(ns->nsim_dev_port->ddir); + ns->nsim_dev_port->ddir = NULL; + free_netdev(dev); } -- 2.47.3