* [PATCH 5.10.y 6.1.y 6.6.y] ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump
@ 2026-08-04 11:46 Pengfei Zhang
2026-08-05 1:13 ` Sasha Levin
0 siblings, 1 reply; 2+ messages in thread
From: Pengfei Zhang @ 2026-08-04 11:46 UTC (permalink / raw)
To: stable
Cc: gregkh, sashal, davem, dsahern, edumazet, kuba, pabeni, idosch,
netdev, linux-kernel, zhangpengfei16, Pengfei Zhang
commit 9facb861dc6b9b9ea9793ef5032a9a826f7a4229 upstream.
inet6_dump_fib() saves its progress in cb->args[1] as a positional
index within the current hash chain. Between batches, a concurrent
fib6_new_table() can insert a new table at the chain head, shifting
all existing entries. The saved index then lands on a different
table, causing fib6_dump_table() to set w->root to the wrong table
while w->node still points into the previous one.
fib6_walk_continue() dereferences w->node->parent (NULL) and panics:
BUG: kernel NULL pointer dereference, address: 0000000000000008
RIP: 0010:fib6_walk_continue+0x6e/0x170
Call Trace:
<TASK>
fib6_dump_table.isra.0+0xc5/0x240
inet6_dump_fib+0xf6/0x420
rtnl_dumpit+0x30/0xa0
netlink_dump+0x15b/0x460
netlink_recvmsg+0x1d6/0x2a0
____sys_recvmsg+0x17a/0x190
Fix by storing tb->tb6_id in cb->args[1] instead of a positional
index. On resume, skip entries until the id matches; a concurrent
head-insert can never match the saved id, so the walker always
resumes on the correct table.
Fixes: 1b43af5480c3 ("[IPV6]: Increase number of possible routing tables to 2^32")
Signed-off-by: Pengfei Zhang <zhangfeionline@gmail.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Link: https://patch.msgid.link/20260625070517.965597-1-zhangfeionline@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
[Adapted to 5.10/6.1/6.6: inet6_dump_fib() there predates 22e36ea9f5d7
and 5fc68320c1fb, so the return variable is "res" not "err" and the
RCU-protected hash walk exits via "out_unlock" instead of "unlock".
Context-only change; the fix itself is identical.]
Signed-off-by: Pengfei Zhang <zhangfeionline@gmail.com>
---
Notes for maintainers, not for the changelog:
This fix was picked up for 5.15.y, 6.12.y, 6.18.y and 7.1.y, but
silently dropped for 5.10.y, 6.1.y and 6.6.y, where it does not apply.
Those three are the only maintained trees still missing it.
The two mainline commits that reshaped inet6_dump_fib() are
22e36ea9f5d7 ("inet: allow ip_valid_fib_dump_req() to be called with
RTNL or RCU") -- v6.9
5fc68320c1fb ("ipv6: remove RTNL protection from inet6_dump_fib()")
-- v6.10
6.12.y and later already contain both, since they branched off after
v6.10. 5.15.y had both backported as prerequisites together with the
fix, which is why the unmodified patch applied there. 5.10.y, 6.1.y and
6.6.y never received them, so the upstream patch no longer applies.
I did not backport those two commits on purpose: they are RTNL
scalability work rather than fixes, and 22e36ea9f5d7 touches six files
including common inet code. Re-contextualising the fix is the smaller
and safer change for these trees.
The race is unaffected by the locking difference -- it happens between
netlink dump batches, when no lock is held at all -- so the older
RTNL-held shape is equally exposed.
inet6_dump_fib() is byte-for-byte identical in 5.10.262, 6.1.180 and
6.6.148, so this single patch covers all three. Build- and boot-tested
on each of them.
net/ipv6/ip6_fib.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -625,11 +625,11 @@
const struct nlmsghdr *nlh = cb->nlh;
struct net *net = sock_net(skb->sk);
unsigned int h, s_h;
- unsigned int e = 0, s_e;
struct fib6_walker *w;
struct fib6_table *tb;
struct hlist_head *head;
int res = 0;
+ u32 s_id;
if (cb->strict_check) {
int err;
@@ -687,25 +687,24 @@
}
s_h = cb->args[0];
- s_e = cb->args[1];
+ s_id = cb->args[1];
rcu_read_lock();
- for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
- e = 0;
+ for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_id = 0) {
head = &net->ipv6.fib_table_hash[h];
hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
- if (e < s_e)
- goto next;
+ if (s_id && tb->tb6_id != s_id)
+ continue;
+
+ s_id = 0;
+ cb->args[1] = tb->tb6_id;
res = fib6_dump_table(tb, skb, cb);
if (res != 0)
goto out_unlock;
-next:
- e++;
}
}
out_unlock:
rcu_read_unlock();
- cb->args[1] = e;
cb->args[0] = h;
out:
res = res < 0 ? res : skb->len;
--
2.54.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH 5.10.y 6.1.y 6.6.y] ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump
2026-08-04 11:46 [PATCH 5.10.y 6.1.y 6.6.y] ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump Pengfei Zhang
@ 2026-08-05 1:13 ` Sasha Levin
0 siblings, 0 replies; 2+ messages in thread
From: Sasha Levin @ 2026-08-05 1:13 UTC (permalink / raw)
To: stable
Cc: Sasha Levin, gregkh, davem, dsahern, edumazet, kuba, pabeni,
idosch, netdev, linux-kernel, zhangpengfei16, Pengfei Zhang
On Tue, Aug 04, 2026 at 07:46:54PM +0800, Pengfei Zhang wrote:
> commit 9facb861dc6b9b9ea9793ef5032a9a826f7a4229 upstream.
>
> inet6_dump_fib() saves its progress in cb->args[1] as a positional
> index within the current hash chain. Between batches, a concurrent
> fib6_new_table() can insert a new table at the chain head, shifting
> all existing entries.
>
> [Adapted to 5.10/6.1/6.6: return variable is 'res' instead of 'err';
> error path uses 'out_unlock' label instead of 'out']
Queued for 5.10, 6.1 and 6.6, thanks.
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-05 1:13 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04 11:46 [PATCH 5.10.y 6.1.y 6.6.y] ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump Pengfei Zhang
2026-08-05 1:13 ` Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox