poll_napi() walks dev->napi_list with list_for_each_entry_rcu(). Some netpoll send paths are already inside an RCU read-side section, but the helper itself does not document or enforce that contract. CONFIG_PROVE_RCU_LIST reports the poll_napi() traversal when the helper is exercised directly from netpoll_poll_dev(). The current source has important lifetime defenses around NAPI deletion and netpoll device close, so this is not presented as a proven use-after-free. The issue is that the RCU-list reader contract is implicit at the helper boundary. Take rcu_read_lock() locally while walking the NAPI list. This keeps the contract with netif_napi_del() and synchronize_net() explicit and avoids relying on every current or future caller to provide the read-side section. This was found by our static analysis tool and then manually reviewed against the current tree. CONFIG_PROVE_RCU_LIST was used as target-matched triage evidence; the RFC is limited to making the helper's RCU-list reader contract explicit. This is an RFC because maintainers may prefer to express the existing netpoll dev_lock/NAPI-list lifetime contract instead of adding a local RCU reader around the polling loop. Signed-off-by: Runyu Xiao --- net/core/netpoll.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 5af14f14a362..2e13ca0d09fe 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -165,12 +165,14 @@ static void poll_napi(struct net_device *dev) struct napi_struct *napi; int cpu = smp_processor_id(); + rcu_read_lock(); list_for_each_entry_rcu(napi, &dev->napi_list, dev_list) { if (cmpxchg(&napi->poll_owner, -1, cpu) == -1) { poll_one_napi(napi); smp_store_release(&napi->poll_owner, -1); } } + rcu_read_unlock(); } void netpoll_poll_dev(struct net_device *dev) -- 2.34.1