Netdev List
 help / color / mirror / Atom feed
* [PATCH net v1] net/ncsi: fix use-after-free of ncsi_dev_priv in ncsi_unregister_dev()
@ 2026-10-17  1:53 Binbin Deng
  2026-10-18  2:36 ` netdev-bot+sashiko
  0 siblings, 1 reply; 2+ messages in thread
From: Binbin Deng @ 2026-10-17  1:53 UTC (permalink / raw)
  To: sam, fercerpav, davem, edumazet, kuba, pabeni, horms, gwshan,
	joel
  Cc: netdev, linux-kernel, Binbin Deng

ncsi_unregister_dev() releases every package (which releases every
channel), unlinks the ncsi_dev_priv from the global ncsi_dev_list with
list_del_rcu(), disables the configuration work and frees the device
itself -- in that order.  Two defects:

  * kfree(ndp) runs immediately after list_del_rcu().  The netlink
    reporting paths resolve the device through ncsi_dev_list
    (ndp_from_ifindex()) and keep dereferencing it, starting with the
    ndp->package_num check at the entry of ncsi_write_package_info(),
    so a reader can be inside the freed allocation.

  * The packages and channels are freed before
    disable_work_sync(&ndp->work); the configuration worker
    (ncsi_dev_work, including set_one_vid()/clear_one_vid()) can be
    walking the very objects being freed, and the teardown has to
    race it instead of quiescing it first.

  writer: rmmod ftgmac100 -> ftgmac100_remove() -> ncsi_unregister_dev()
            -> ncsi_remove_package() ... -> kfree(ndp)
  reader: sendmsg(NETLINK_GENERIC, NCSI_CMD_PKG_INFO)
            -> ncsi_pkg_info_nl() -> ncsi_write_package_info()

Quiesce the work first, then release the packages, and defer the free
of the ncsi_dev_priv with kfree_rcu().  A corollary of the old
ordering: the ftgmac100 driver calls ncsi_stop_dev() right after
ncsi_unregister_dev() and used to read the already freed device.

Commit a0ffa68c70b3 ("net/ncsi: Disable the ncsi work before freeing
the associated structure") already added a disable_work_sync() before
the kfree(ndp), but its placement still releases the packages and
channels first, so the worker can walk those while they are being
freed, and it does nothing for the netlink readers.

Both defects are visible in KASAN.  The netlink reader, on the
ndp->package_num entry check of ncsi_write_package_info() -- a read of
size 4 at ndp+160; struct ncsi_dev_priv is 17700 bytes on arm32, hence
kmalloc-32k:

  BUG: KASAN: use-after-free in ncsi_write_package_info+0xf50/0xf84
  Read of size 4 at addr c9f100a0 by task poc_dump/62
  Call trace:
   ...
   kasan_report from ncsi_write_package_info+0xf50/0xf84
   ncsi_write_package_info from ncsi_pkg_info_nl+0x320/0x63c
   ncsi_pkg_info_nl from genl_family_rcv_msg_doit+0x218/0x2d8
   genl_family_rcv_msg_doit from genl_rcv_msg+0x578/0xb24
   genl_rcv_msg from netlink_rcv_skb+0x134/0x3c4
   netlink_rcv_skb from genl_rcv+0x24/0x34
   genl_rcv from netlink_unicast+0x884/0xac0
   netlink_unicast from netlink_sendmsg+0x62c/0xb98
   netlink_sendmsg from __sys_sendto+0x290/0x3e0
   __sys_sendto from sys_send+0x18/0x20
   sys_send from ret_fast_syscall+0x0/0x5c
  The buggy address belongs to the physical page:

(a page-level report: the freed pages had already been returned to the
buddy allocator, so no cache or object lines are printed).  The same
reader site also surfaces once the page has been recycled into another
cache -- the same free at a different stage of the page lifecycle:

  BUG: KASAN: slab-out-of-bounds in ncsi_write_package_info+0xf50/0xf84
  Read of size 4 at addr c9d700a0 by task poc_dump/70
  Call trace:
   ...
   kasan_report from ncsi_write_package_info+0xf50/0xf84
   ncsi_write_package_info from ncsi_pkg_info_nl+0x320/0x63c
   ...
   netlink_unicast from netlink_sendmsg+0x62c/0xb98
  The buggy address belongs to the object at c9d70000
   which belongs to the cache kmalloc-8k of size 8192
  The buggy address is located 160 bytes inside of
   allocated 8192-byte region [c9d70000, c9d72000)

And the corollary, in the rmmod task itself -- its stack documents the
very ordering this patch changes (ncsi_unregister_dev() has already
freed the device when unregister_netdev() closes it):

  BUG: KASAN: use-after-free in ncsi_stop_dev+0x398/0x3a0
  Read of size 4 at addr c9d480a8 by task poc_dump/55
  Call trace:
   ...
   kasan_report from ncsi_stop_dev+0x398/0x3a0
   ncsi_stop_dev from ftgmac100_stop+0x2dc/0x364 [ftgmac100]
   ftgmac100_stop [ftgmac100] from __dev_close_many+0x2d8/0x57c
   __dev_close_many from netif_close_many+0x264/0x5a4
   netif_close_many from unregister_netdevice_many_notify+0x87c/0x1f0c
   unregister_netdevice_many_notify from
    unregister_netdevice_queue+0x290/0x328
   unregister_netdevice_queue from unregister_netdev+0x18/0x68
   unregister_netdev from ftgmac100_remove+0x74/0x9c [ftgmac100]
   ftgmac100_remove [ftgmac100] from
    device_release_driver_internal+0x41c/0x5e0
   ...
   bus_remove_driver from sys_delete_module+0x308/0x438
   sys_delete_module from ret_fast_syscall+0x0/0x5c

Tested on an arm multi_v7 kernel (KASAN + SLUB + PREEMPT) running as a
QEMU ast2600-evb guest with a host-side NC-SI responder; with the
series applied the same rmmod hammering completes without KASAN
reports.

Found during a static-analysis-assisted audit: candidate sites were
surfaced by a Coccinelle rule, and the reader/writer paths were
confirmed by LLM-assisted (GLM-5.3) code inspection.

Fixes: 2d283bdd079c ("net/ncsi: Resource management")
Signed-off-by: Binbin Deng <18983559317@163.com>
---
 net/ncsi/internal.h    | 1 +
 net/ncsi/ncsi-manage.c | 9 ++++++---
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index adee6dcabdc3..5d315768409a 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -316,6 +316,7 @@ struct vlan_vid {
 
 struct ncsi_dev_priv {
 	struct ncsi_dev     ndev;            /* Associated NCSI device     */
+	struct rcu_head     rcu;             /* Deferred free (kfree_rcu)  */
 	unsigned int        flags;           /* NCSI device flags          */
 #define NCSI_DEV_PROBED		1            /* Finalized NCSI topology    */
 #define NCSI_DEV_HWA		2            /* Enabled HW arbitration     */
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 54d0df0a9efe..35e9dd85e044 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -1960,6 +1960,11 @@ void ncsi_unregister_dev(struct ncsi_dev *nd)
 
 	dev_remove_pack(&ndp->ptype);
 
+	/* Quiesce the configuration worker before releasing the packages
+	 * and channels it walks.
+	 */
+	disable_work_sync(&ndp->work);
+
 	list_for_each_entry_safe(np, tmp, &ndp->packages, node)
 		ncsi_remove_package(np);
 
@@ -1967,8 +1972,6 @@ void ncsi_unregister_dev(struct ncsi_dev *nd)
 	list_del_rcu(&ndp->node);
 	spin_unlock_irqrestore(&ncsi_dev_lock, flags);
 
-	disable_work_sync(&ndp->work);
-
-	kfree(ndp);
+	kfree_rcu(ndp, rcu);
 }
 EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-18  2:37 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17  1:53 [PATCH net v1] net/ncsi: fix use-after-free of ncsi_dev_priv in ncsi_unregister_dev() Binbin Deng
2026-10-18  2:36 ` netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox