RE: [PATCH net] tipc: fix use-after-free of discoverer in tipc_disc_rcv()
From: Tung Quang Nguyen
Date: Fri Jun 12 2026 - 04:54:10 EST
>Subject: [PATCH net] tipc: fix use-after-free of discoverer in tipc_disc_rcv()
>
>bearer_disable() frees b->disc with tipc_disc_delete()'s plain kfree(), but
>tipc_disc_rcv() still dereferences b->disc in RX softirq under
>rcu_read_lock() (tipc_udp_recv -> tipc_rcv -> tipc_disc_rcv).
>
>L2 bearers are safe thanks to the synchronize_net() in tipc_disable_l2_media(),
>but the UDP bearer defers that call to the
>cleanup_bearer() workqueue, so the discoverer is freed with no grace
>period:
>
> BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149)
>Read of size 8 at addr ffff88802348b728 by task poc_tipc/184 <IRQ>
> tipc_disc_rcv (net/tipc/discover.c:149)
> tipc_rcv (net/tipc/node.c:2126)
> tipc_udp_recv (net/tipc/udp_media.c:391)
> udp_rcv (net/ipv4/udp.c:2643)
> ip_local_deliver_finish (net/ipv4/ip_input.c:241) </IRQ> Freed by task 181:
> kfree (mm/slub.c:6565)
> bearer_disable (net/tipc/bearer.c:418)
> tipc_nl_bearer_disable (net/tipc/bearer.c:1001)
>
>The bearer is freed with kfree_rcu(); free the discoverer the same way.
>Add an rcu_head to struct tipc_discoverer and free it and its skb from an RCU
>callback.
>
>Reachable from an unprivileged user namespace: the TIPCv2 genl family is
>netnsok and its bearer commands have no GENL_ADMIN_PERM. Needs
>CONFIG_TIPC and CONFIG_TIPC_MEDIA_UDP.
>
>Fixes: 25b0b9c4e835 ("tipc: handle collisions of 32-bit node address hash
>values")
>Reported-by: Xiang Mei <xmei5@xxxxxxx>
>Assisted-by: Claude:claude-opus-4-8
>Signed-off-by: Weiming Shi <bestswngs@xxxxxxxxx>
>---
> net/tipc/discover.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
>diff --git a/net/tipc/discover.c b/net/tipc/discover.c index
>3e54d2df5683a..34dbe5ad10e09 100644
>--- a/net/tipc/discover.c
>+++ b/net/tipc/discover.c
>@@ -58,6 +58,7 @@
> * @skb: request message to be (repeatedly) sent
> * @timer: timer governing period between requests
> * @timer_intv: current interval between requests (in ms)
>+ * @rcu: RCU head for deferred freeing
> */
> struct tipc_discoverer {
> u32 bearer_id;
>@@ -69,6 +70,7 @@ struct tipc_discoverer {
> struct sk_buff *skb;
> struct timer_list timer;
> unsigned long timer_intv;
>+ struct rcu_head rcu;
> };
>
> /**
>@@ -382,6 +384,14 @@ int tipc_disc_create(struct net *net, struct tipc_bearer
>*b,
> return 0;
> }
>
>+static void tipc_disc_free_rcu(struct rcu_head *rp) {
>+ struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer,
>+rcu);
This line is long (over 80 columns). Please break it into 2 lines (refer to linux/Documentation/process/coding-style.rst).
>+
>+ kfree_skb(d->skb);
>+ kfree(d);
>+}
>+
> /**
> * tipc_disc_delete - destroy object sending periodic link setup requests
> * @d: ptr to link dest structure
>@@ -389,8 +399,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer
>*b, void tipc_disc_delete(struct tipc_discoverer *d) {
> timer_shutdown_sync(&d->timer);
>- kfree_skb(d->skb);
>- kfree(d);
>+ call_rcu(&d->rcu, tipc_disc_free_rcu);
> }
>
> /**
>--
>2.43.0
>