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 <bobbyeshleman@meta.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260903221154.3359889-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-03 15:11:54 -07:00
parent daf0972d38
commit 761ae184f8
2 changed files with 23 additions and 7 deletions

View File

@ -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)

View File

@ -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);