mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net v2] net/ncsi: fix use-after-free of ncsi_package in ncsi_remove_package()
@ 2026-10-19  1:36 Binbin Deng
  2026-10-20  2:25 ` netdev-bot+sashiko
  0 siblings, 1 reply; 2+ messages in thread
From: Binbin Deng @ 2026-10-19  1:36 UTC (permalink / raw)
  To: sam, fercerpav, davem, edumazet, kuba, pabeni, horms, gwshan, joel
  Cc: netdev, linux-kernel, Binbin Deng

ncsi_remove_package() unlinks the package from ndp->packages with
list_del_rcu() and then frees it with kfree(), after having released
all of its channels.  The generic netlink package info walkers iterate
the same list with the list_for_each_entry_rcu() based
NCSI_FOR_EACH_PACKAGE() and dereference the package in the loop body:

    rc = nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);

None of the NCSI genl handlers enters an RCU read-side critical
section: genl doit and dumpit callbacks run under genl_mutex and
cb_mutex, not under rcu_read_lock(), and ncsi_unregister_dev() takes
neither.  A reader that is preempted mid-walk -- or one that has
resolved the device and then sleeps in the genlmsg_new(GFP_KERNEL)
allocation of ncsi_pkg_info_nl() -- has no protection at all when the
teardown frees the package under it.

The race needs no real NC-SI hardware and is reachable by an
unprivileged user, as NCSI_CMD_PKG_INFO has no GENL_ADMIN_PERM:

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

ftgmac100_remove() calls ncsi_unregister_dev() before
unregister_netdev(), so the netdev, and with it the genl entry point,
stays reachable during the whole window.

KASAN report (arm multi_v7, QEMU ast2600-evb, KASAN + SLUB + PREEMPT,
four threads looping PKG_INFO doit/dump while cycling insmod/rmmod):

  BUG: KASAN: slab-use-after-free in ncsi_write_package_info+0xe3c/0xf84
  Read of size 1 at addr c69e2580 by task poc_dump/69
  The buggy address belongs to the object at c69e2580
   which belongs to the cache kmalloc-64 of size 64
  The buggy address is located 0 bytes inside of
   freed 64-byte region [c69e2580, c69e25c0)

The read is the u8 np->id feeding the nla_put_u32() above;
sizeof(struct ncsi_package) is 60 bytes, so it lands in kmalloc-64,
and with no VLAN churn in this mode the only size-1 read at offset 0
of that cache on this path is np->id.

Free the package with kfree_rcu() so that it survives the grace
period, and give struct ncsi_package an rcu_head.

This is the free side of the fix for the package object.  The read
side -- rcu_read_lock() around the device resolution and the list
walks of all NCSI genl handlers -- is the companion series "net/ncsi:
fix use-after-free of ncsi_channel in ncsi_remove_channel()"; the
parent ncsi_dev_priv is deferred by the companion series "net/ncsi:
fix use-after-free of ncsi_dev_priv in ncsi_unregister_dev()".  The
race is closed by the three series together; each one keeps its own
Fixes: tag so they can be backported independently.

Found by a Coccinelle rule matching kfree() immediately after
list_del_rcu(); the code inspection and the fix were LLM-assisted,
and the race was confirmed at runtime with KASAN as above.

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

diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index adee6dc..cb41953 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -253,6 +253,7 @@ struct ncsi_package {
 	bool                 multi_channel; /* Enable multiple channels  */
 	u32                  channel_whitelist; /* Channels to configure */
 	struct ncsi_channel  *preferred_channel; /* Primary channel      */
+	struct rcu_head         rcu;
 };
 
 struct ncsi_request {
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 54d0df0..237e041 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -326,7 +326,8 @@ void ncsi_remove_package(struct ncsi_package *np)
 	ndp->package_num--;
 	spin_unlock_irqrestore(&ndp->lock, flags);
 
-	kfree(np);
+	/* Netlink walkers may still hold the package */
+	kfree_rcu(np, rcu);
 }
 
 void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
-- 
2.43.0


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

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

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-19  1:36 [PATCH net v2] net/ncsi: fix use-after-free of ncsi_package in ncsi_remove_package() Binbin Deng
2026-10-20  2:25 ` 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

all inboxes | Powered by JetHome®