Re: [PATCH RFC] netdevsim: remove debugfs files before freeing net_device

Aleksandr Nogikh <[email protected]>
Newsgroups dev.linux.lists.syzbot
Message-ID <CANp29Y6o2PzZVMeS5w=MAz22kKE5sRBPZ5_9xrpmZ6zeU5T03A@mail.gmail.com>
On Tue, Jun 23, 2026 at 4:31 PM 'syzbot' via
syzkaller-upstream-moderation
<[email protected]> wrote:
>
> A KASAN slab-use-after-free was detected in debugfs_u32_get() when reading
> a debugfs file associated with a netdevsim port.
>
> BUG: KASAN: slab-use-after-free in debugfs_u32_get+0x6c/0x70
> Read of size 4 at addr ffff88810cd6db00
>
> Call Trace:
>  debugfs_u32_get+0x6c/0x70
>  simple_attr_read+0x227/0x490
>  debugfs_attr_read+0x76/0x130
>  vfs_read+0x213/0xa80
>
> The root cause is that the net_device (and its embedded netdevsim private
> data) is freed before its associated debugfs files are removed. When a user
> reads a debugfs file like rx_max_pending, debugfs_u32_get() dereferences a
> pointer to the freed memory. This happens because nsim_destroy() calls
> free_netdev() but does not remove the debugfs files created by
> nsim_ethtool_init() or nsim_bpf_init(). These files are only removed later
> when nsim_dev_port_debugfs_exit() is called. A similar issue exists in the
> error path of nsim_create().
>
> To fix this, swap the teardown order in __nsim_dev_port_del() and the
> __nsim_dev_port_add() error path so that nsim_dev_port_debugfs_exit() is
> called before nsim_destroy(). This ensures the debugfs directory is
> recursively removed before the net_device is freed. Additionally, remove
> explicit debugfs_remove() calls from various subsystem teardown functions
> (like nsim_psp_uninit, nsim_ipsec_teardown, etc.) since the parent
> directory is now removed earlier, which would otherwise lead to a
> use-after-free of the dentry itself. Finally, ensure the debugfs directory
> is removed before free_netdev() is called in the nsim_create() error path.
>
> 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=84a7c962-d10e-424d-a37a-bf27d4830a40
> 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]>
>
> ---
> 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;

goto to goto looks very weird..
Maybe we can simplify it with an `if (nsim_dev_port->ns != NULL)`
check? Then `err_nsim_destroy` could be dropped.

>  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);

Now that vlan_dfs, qr_dfs, pp_dfs are not used for debugfs_remove, do
we actually still need those fields?

> -
>         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.