From 761ae184f850f33d1bbf6c4530c7f237be780d21 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Sep 2026 15:11:54 -0700 Subject: [PATCH] netdev: avoid skipping objects on race with device disappearance If the currently dumped device disappears while we were mid-dump we will get the next device without resetting the sub-object ID. This is quite unlikely, it was reported by an AI tool not a real user. Let's fix it for better dump consistency. We only intend to cover the case where device A disappears and we skip over sub-objects of device B which was stable (B existed before and after the dump). We don't intend to provide stable results for any device that gets created or deleted during the dump. Reviewed-by: Bobby Eshleman Acked-by: Daniel Borkmann Reviewed-by: Nikolay Aleksandrov Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260903221154.3359889-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/core/netdev-genl.c | 22 +++++++++++++++++----- net/core/page_pool_user.c | 8 ++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index b8ba91cc93da..fa9edfdb32c2 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -312,11 +312,14 @@ int netdev_nl_napi_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) err = -ENODEV; } } else { + unsigned long start_ifindex = ctx->ifindex; + for_each_netdev_lock_scoped(net, netdev, ctx->ifindex) { + if (ctx->ifindex != start_ifindex) + ctx->napi_id = 0; err = netdev_nl_napi_dump_one(netdev, skb, info, ctx); if (err < 0) break; - ctx->napi_id = 0; } } @@ -636,13 +639,17 @@ int netdev_nl_queue_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) err = -ENODEV; } } else { + unsigned long start_ifindex = ctx->ifindex; + for_each_netdev_lock_ops_compat_scoped(net, netdev, ctx->ifindex) { + if (ctx->ifindex != start_ifindex) { + ctx->rxq_idx = 0; + ctx->txq_idx = 0; + } err = netdev_nl_queue_dump_one(netdev, skb, info, ctx); if (err < 0) break; - ctx->rxq_idx = 0; - ctx->txq_idx = 0; } } @@ -788,8 +795,6 @@ netdev_nl_stats_by_queue(struct net_device *netdev, struct sk_buff *rsp, ctx->txq_idx = ++i; } - ctx->rxq_idx = 0; - ctx->txq_idx = 0; return 0; } @@ -904,6 +909,7 @@ int netdev_nl_qstats_get_dumpit(struct sk_buff *skb, struct netdev_nl_dump_ctx *ctx = netdev_dump_ctx(cb); const struct genl_info *info = genl_info_dump(cb); struct net *net = sock_net(skb->sk); + unsigned long start_ifindex; struct net_device *netdev; unsigned int ifindex; unsigned int scope; @@ -936,7 +942,13 @@ int netdev_nl_qstats_get_dumpit(struct sk_buff *skb, return err; } + start_ifindex = ctx->ifindex; + for_each_netdev_lock_ops_compat_scoped(net, netdev, ctx->ifindex) { + if (ctx->ifindex != start_ifindex) { + ctx->rxq_idx = 0; + ctx->txq_idx = 0; + } err = netdev_nl_qstats_get_dump_one(netdev, scope, skb, info, ctx); if (err < 0) diff --git a/net/core/page_pool_user.c b/net/core/page_pool_user.c index ef4261c0e8ea..f30f3779624c 100644 --- a/net/core/page_pool_user.c +++ b/net/core/page_pool_user.c @@ -84,6 +84,7 @@ netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb, struct page_pool_dump_cb *state = (void *)cb->ctx; const struct genl_info *info = genl_info_dump(cb); struct net *net = sock_net(skb->sk); + unsigned long start_ifindex; struct net_device *netdev; struct page_pool *pool; int err = 0; @@ -91,6 +92,8 @@ netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb, if (ifindex_attr) state->ifindex = nla_get_u32(ifindex_attr); + start_ifindex = state->ifindex; + rtnl_lock(); mutex_lock(&page_pools_lock); for_each_netdev_dump(net, netdev, state->ifindex) { @@ -99,6 +102,9 @@ netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb, netdev->ifindex != nla_get_u32(ifindex_attr)) break; + if (state->ifindex != start_ifindex) + state->pp_id = 0; + hlist_for_each_entry(pool, &netdev->page_pools, user.list) { if (state->pp_id && state->pp_id < pool->user.id) continue; @@ -108,8 +114,6 @@ netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb, if (err) goto out; } - - state->pp_id = 0; } out: mutex_unlock(&page_pools_lock);