Skip to content

Commit 5e215bf

Browse files
winmingregkh
authored andcommitted
tipc: fix use-after-free of the discoverer in tipc_disc_rcv()
[ Upstream commit 1579342 ] 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. Because the RCU callback (tipc_disc_free_rcu) lives in module text, a call_rcu() that is still pending when the tipc module is unloaded would invoke a freed function. Add an rcu_barrier() to tipc_exit() after the bearer subsystem has been torn down, so all pending discoverer callbacks have run before the module text goes away. 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: 25b0b9c ("tipc: handle collisions of 32-bit node address hash values") Reported-by: Xiang Mei <xmei5@asu.edu> Signed-off-by: Weiming Shi <bestswngs@gmail.com> Reviewed-by: Tung Nguyen <tung.quang.nguyen@est.tech> Link: https://patch.msgid.link/20260617135744.3383175-3-bestswngs@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent a07f77e commit 5e215bf

2 files changed

Lines changed: 17 additions & 2 deletions

File tree

net/tipc/core.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ static void __exit tipc_exit(void)
218218
unregister_pernet_device(&tipc_net_ops);
219219
tipc_unregister_sysctl();
220220

221+
/* TODO: Wait for all timers that called call_rcu() to finish before
222+
* calling rcu_barrier().
223+
*/
224+
rcu_barrier();
225+
221226
pr_info("Deactivated\n");
222227
}
223228

net/tipc/discover.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
* @skb: request message to be (repeatedly) sent
5959
* @timer: timer governing period between requests
6060
* @timer_intv: current interval between requests (in ms)
61+
* @rcu: RCU head for deferred freeing
6162
*/
6263
struct tipc_discoverer {
6364
u32 bearer_id;
@@ -69,6 +70,7 @@ struct tipc_discoverer {
6970
struct sk_buff *skb;
7071
struct timer_list timer;
7172
unsigned long timer_intv;
73+
struct rcu_head rcu;
7274
};
7375

7476
/**
@@ -382,15 +384,23 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,
382384
return 0;
383385
}
384386

387+
static void tipc_disc_free_rcu(struct rcu_head *rp)
388+
{
389+
struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer,
390+
rcu);
391+
392+
kfree_skb(d->skb);
393+
kfree(d);
394+
}
395+
385396
/**
386397
* tipc_disc_delete - destroy object sending periodic link setup requests
387398
* @d: ptr to link dest structure
388399
*/
389400
void tipc_disc_delete(struct tipc_discoverer *d)
390401
{
391402
timer_shutdown_sync(&d->timer);
392-
kfree_skb(d->skb);
393-
kfree(d);
403+
call_rcu(&d->rcu, tipc_disc_free_rcu);
394404
}
395405

396406
/**

0 commit comments

Comments
 (0)