[PATCH net 1/1] tipc: avoid use-after-free in trace queue dumps
From: Ren Wei <hidden>
Date: 2026-07-20 06:45:31
Subsystem:
networking [general], the rest, tipc network layer · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds, Jon Maloy
From: Zihan Xi <redacted>
TIPC trace helpers can dump live socket queues from contexts such as
poll(). tipc_list_dump() walks the write and receive skb queues and dumps
the head, tail or selected skb entries, while tipc_sk_dump() also dumps the
socket backlog head and tail directly.
Those trace paths do not have a reliable skb lifetime guarantee for every
queue they inspect. TIPC socket queues are serialized by socket or other
outer locks, and not all skb lists use the embedded sk_buff_head lock
as the mutator lock. A concurrent dequeue/free can therefore leave the
trace helper dereferencing a stale skb pointer.
Avoid dereferencing queue members from the socket trace dump path. Keep the
trace ABI and buffer sizing unchanged, but report only queue lengths
for the write queue, receive queue, and backlog. This preserves useful
queue state in trace output without pretending that the trace helper can
safely pin or walk all caller-provided lists.
Fixes: b4b9771bcbbd ("tipc: enable tracepoints in tipc")
Cc: stable@vger.kernel.org
Reported-by: Vega <redacted>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zihan Xi <redacted>
Reviewed-by: Ren Wei <redacted>
---
net/tipc/socket.c | 12 +++---------
net/tipc/trace.c | 44 +++++---------------------------------------
2 files changed, 8 insertions(+), 48 deletions(-)
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index e564341e0216..4b9eb0b401fe 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c@@ -4000,15 +4000,9 @@ int tipc_sk_dump(struct sock *sk, u16 dqueues, char *buf) i += tipc_list_dump(&sk->sk_receive_queue, false, buf + i); } - if (dqueues & TIPC_DUMP_SK_BKLGQ) { - i += scnprintf(buf + i, sz - i, "sk_backlog:\n head "); - i += tipc_skb_dump(sk->sk_backlog.head, false, buf + i); - if (sk->sk_backlog.tail != sk->sk_backlog.head) { - i += scnprintf(buf + i, sz - i, " tail "); - i += tipc_skb_dump(sk->sk_backlog.tail, false, - buf + i); - } - } + if (dqueues & TIPC_DUMP_SK_BKLGQ) + i += scnprintf(buf + i, sz - i, "sk_backlog: len = %d\n", + READ_ONCE(sk->sk_backlog.len)); return i; }
diff --git a/net/tipc/trace.c b/net/tipc/trace.c
index 7d2931521e0e..399538d0a369 100644
--- a/net/tipc/trace.c
+++ b/net/tipc/trace.c@@ -158,49 +158,15 @@ int tipc_skb_dump(struct sk_buff *skb, bool more, char *buf) /** * tipc_list_dump - dump TIPC skb list/queue * @list: list of skbs to be dumped - * @more: dump more? - * - false: dump only the head & tail skbs - * - true: dump the first & last 5 skbs + * @more: unused; kept for tracepoint ABI compatibility * @buf: returned buffer of dump data in format */ int tipc_list_dump(struct sk_buff_head *list, bool more, char *buf) { - int i = 0; - size_t sz = (more) ? LIST_LMAX : LIST_LMIN; - u32 count, len; - struct sk_buff *hskb, *tskb, *skb, *tmp; - - if (!list) { - i += scnprintf(buf, sz, "(null)\n"); - return i; - } + size_t sz = more ? LIST_LMAX : LIST_LMIN; - len = skb_queue_len(list); - i += scnprintf(buf, sz, "len = %d\n", len); + if (!list) + return scnprintf(buf, sz, "(null)\n"); - if (!len) - return i; - - if (!more) { - hskb = skb_peek(list); - i += scnprintf(buf + i, sz - i, " head "); - i += tipc_skb_dump(hskb, false, buf + i); - if (len > 1) { - tskb = skb_peek_tail(list); - i += scnprintf(buf + i, sz - i, " tail "); - i += tipc_skb_dump(tskb, false, buf + i); - } - } else { - count = 0; - skb_queue_walk_safe(list, skb, tmp) { - count++; - if (count == 6) - i += scnprintf(buf + i, sz - i, " .\n .\n"); - if (count > 5 && count <= len - 5) - continue; - i += scnprintf(buf + i, sz - i, " #%d ", count); - i += tipc_skb_dump(skb, false, buf + i); - } - } - return i; + return scnprintf(buf, sz, "len = %u\n", skb_queue_len_lockless(list)); }
--
2.43.0