From: Jun Yang <juny24602@gmail.com>
To: dsahern@kernel.org, idosch@nvidia.com
Cc: davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, kaber@trash.net,
netdev@vger.kernel.org, Jun Yang <junvyyang@tencent.com>,
stable@kernel.org, TencentOS Corvus AI <corvus@tencent.com>
Subject: [PATCH net] ipv6: take tb6_lock across the fib6 dump sernum snapshot
Date: Mon, 12 Oct 2026 16:38:20 +0800 [thread overview]
Message-ID: <20261012083843.60510-1-juny24602@gmail.com> (raw)
From: Jun Yang <junvyyang@tencent.com>
A resumable RTM_GETROUTE dump parks a raw struct fib6_info pointer in
w->leaf when the skb fills (fib6_dump_node()) and resumes from it on the
next recvmsg(). No reference is held across the suspension; the only
thing standing between the walker and a freed route is the fn_sernum
check in fib6_dump_table(), which restarts from the root when the tree
changed.
That check is done outside tb6_lock at both ends: the snapshot after the
first walk is taken after spin_unlock_bh(), and on continuation the
compare runs before spin_lock_bh(). A writer that slips in between, e.g.
RTM_NEWROUTE with NLM_F_REPLACE on the route the walker is parked on,
frees the route via fib6_info_release() (the replace path has no
FOR_WALKERS fixup, unlike fib6_del_route()) and bumps fn_sernum, yet the
dump records the new serial next to the stale w->leaf, or passes the
compare and only then takes the lock. Either way the restart is skipped
and fib6_walk_continue() hands the freed route to rt6_fill_node():
BUG: KASAN: slab-use-after-free in rt6_fill_node (net/ipv6/route.c:5821)
Read of size 4 at addr ffff88810adb0c64 by task fib6dump-repro/541
CPU: 0 UID: 1000 PID: 541 Comm: fib6dump-repro Not tainted 7.3.0-rc6 #1 PREEMPT
Call Trace:
rt6_fill_node (net/ipv6/route.c:5821)
rt6_dump_route (net/ipv6/route.c:6130)
fib6_dump_node (net/ipv6/ip6_fib.c:534)
fib6_walk_continue (net/ipv6/ip6_fib.c:2180)
fib6_dump_table (net/ipv6/ip6_fib.c:620)
inet6_dump_fib (net/ipv6/ip6_fib.c:714)
rtnl_dumpit (net/core/rtnetlink.c:7006)
netlink_dump (net/netlink/af_netlink.c:2350)
netlink_recvmsg (net/netlink/af_netlink.c:2001)
sock_recvmsg (net/socket.c:1173)
__sys_recvfrom (net/socket.c:2331)
Take tb6_lock once at the top of fib6_dump_table() and hold it across
the snapshot, the comparison and the walker call, so fn_sernum is read
in the same critical section as the walk it describes.
Fixes: 2bec5a369ee7 ("ipv6: fib: fix crash when changing large fib while dumping it")
Cc: stable@kernel.org
Reported-by: TencentOS Corvus AI <corvus@tencent.com>
Assisted-by: tencentos-corvus-ai:hy4-preview
Signed-off-by: Jun Yang <junvyyang@tencent.com>
---
A KASAN reproducer for this issue is available if requested.
net/ipv6/ip6_fib.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 9ff761962b45..70482381165f 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -592,14 +592,13 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
w = (void *)cb->args[2];
w->root = &table->tb6_root;
+ spin_lock_bh(&table->tb6_lock);
if (cb->args[4] == 0) {
w->count = 0;
w->skip = 0;
w->skip_in_node = 0;
- spin_lock_bh(&table->tb6_lock);
res = fib6_walk(net, w);
- spin_unlock_bh(&table->tb6_lock);
if (res > 0) {
cb->args[4] = 1;
cb->args[5] = READ_ONCE(w->root->fn_sernum);
@@ -616,14 +615,13 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
} else
w->skip = 0;
- spin_lock_bh(&table->tb6_lock);
res = fib6_walk_continue(w);
- spin_unlock_bh(&table->tb6_lock);
if (res <= 0) {
fib6_walker_unlink(net, w);
cb->args[4] = 0;
}
}
+ spin_unlock_bh(&table->tb6_lock);
return res;
}
base-commit: a90ee4305c4a5df72c11b31dacfdc76e00fcf78a
--
2.43.7
next reply other threads:[~2026-10-12 8:39 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 8:38 Jun Yang [this message]
2026-10-13 8:41 ` [PATCH net] ipv6: take tb6_lock across the fib6 dump sernum snapshot netdev-bot+sashiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261012083843.60510-1-juny24602@gmail.com \
--to=juny24602@gmail.com \
--cc=corvus@tencent.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=junvyyang@tencent.com \
--cc=kaber@trash.net \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox