[PATCH net v6 2/3] tipc: fix NULL deref in deferred bulk distribution on publish failure

From: Weiming Shi

Date: Fri Jul 17 2026 - 14:58:39 EST


tipc_net_finalize() does not check the return value of
tipc_nametbl_publish(). If the publish fails, for example on a
GFP_ATOMIC allocation failure, the node state name never lands in
cluster_scope, but tn->finalized is still set. A worker deferred by
tipc_named_node_up() then wakes and calls named_distribute() with an
empty list. That replays the same unguarded buf_msg(skb_peek_tail(list))
tail stamp, this time on the tipc_node_dist_bulk workqueue:

KASAN: null-ptr-deref in range [0x00000000000000c8-0x00000000000000cf]
RIP: 0010:named_distribute (net/tipc/name_distr.c:200)
Workqueue: events tipc_node_dist_bulk
Call Trace:
tipc_named_dist_cluster_scope (net/tipc/name_distr.c:267)
tipc_node_dist_bulk (net/tipc/node.c:403)
process_one_work
worker_thread
Kernel panic - not syncing: Fatal exception in interrupt

Check the publish result and warn on failure, but still set finalized,
otherwise deferred workers would sleep forever. In
tipc_named_dist_cluster_scope() re-check cluster_scope after the wait
and skip the distribution when it is empty. This is a permanent
condition, so return 0 instead of an error, otherwise the link would
be bounced forever. Also guard the tail stamp in named_distribute()
itself, so a caller that misses the precondition gets a warning and a
link reset through the existing -ENOBUFS path instead of a crash.

Reproducing this needs an allocation failure during finalize, so I
verified it by stubbing out the publish call: both nodes log the
failure, the workers skip the distribution, no crash, no link flap.
The normal path is unchanged with the same two-node test.

Fixes: cad2929dc432 ("tipc: update a binding service via broadcast")
Reported-by: Xiang Mei <xmei5@xxxxxxx>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Weiming Shi <bestswngs@xxxxxxxxx>
---
net/tipc/name_distr.c | 11 +++++++++++
net/tipc/net.c | 3 ++-
2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index b764274df758..5b0fb09226fc 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -193,6 +193,10 @@ static int named_distribute(struct net *net, struct sk_buff_head *list,
skb_trim(skb, INT_H_SIZE + (msg_dsz - msg_rem));
__skb_queue_tail(list, skb);
}
+ if (skb_queue_empty(list)) {
+ pr_warn("Bulk publication list empty, nothing to distribute\n");
+ return 1;
+ }
hdr = buf_msg(skb_peek_tail(list));
msg_set_last_bulk(hdr);
msg_set_named_seqno(hdr, seqno);
@@ -253,6 +257,13 @@ int tipc_named_dist_cluster_scope(struct net *net, u32 dnode)
spin_unlock_bh(&tn->nametbl_lock);

read_lock_bh(&nt->cluster_scope_lock);
+ if (unlikely(list_empty(&nt->cluster_scope))) {
+ /* finalize is done but nothing was published (publish
+ * failed): a permanent state, nothing to synchronize.
+ */
+ read_unlock_bh(&nt->cluster_scope_lock);
+ return 0;
+ }
if (named_distribute(net, &head, dnode, &nt->cluster_scope, seqno)) {
read_unlock_bh(&nt->cluster_scope_lock);
return -ENOBUFS;
diff --git a/net/tipc/net.c b/net/tipc/net.c
index 4c144e720ac1..2aa8812c551a 100644
--- a/net/tipc/net.c
+++ b/net/tipc/net.c
@@ -138,7 +138,8 @@ static void tipc_net_finalize(struct net *net, u32 addr)
tipc_named_reinit(net);
tipc_sk_reinit(net);
tipc_mon_reinit_self(net);
- tipc_nametbl_publish(net, &ua, &sk, addr);
+ if (!tipc_nametbl_publish(net, &ua, &sk, addr))
+ pr_warn("Failed to publish own node state\n");
atomic_inc(&tn->finalized);
wake_up_var(&tn->finalized);
}
--
2.43.0