amt_dev_stop() clears amt->sk and waits in synchronize_net(), but the socket's sk_user_data still points at the device until udp_tunnel_sock_release() clears it afterwards, and nothing waits for readers then. An amt_rcv() that loads the pointer after the grace period can still be running once amt_dev_stop() has cancelled event_wq and torn down the relay's tunnels. A gateway can then queue a received Advertisement or Membership Query on event_wq again, and a relay can link a new tunnel from a Request and arm its gc_wq. Either work can then run after the device has been freed. Clear sk_user_data before the grace period, so synchronize_net() also waits for every amt_rcv() that can still see the device. Once it is NULL, amt_rcv() drops the packet and amt_err_lookup() ignores the ICMP error. The race is as old as amt_rcv(). From commit 3cf7203ca620 ("net/tunnel: wait until all sk_user_data reader finish before releasing the sock") until commit 72d3b9a4c2b1 ("udp_tunnel: Remove synchronize_rcu() in udp_tunnel_sock_release()."), udp_tunnel_sock_release() waited for those readers after clearing sk_user_data, which hid it. The wait was removed because each tunnel driver frees its fast-path state only after a grace period, but amt_rcv() does more than read that state: it queues work and links tunnels that amt_dev_stop() must already have stopped. Found by code inspection; not triggered. Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface") Fixes: 72d3b9a4c2b1 ("udp_tunnel: Remove synchronize_rcu() in udp_tunnel_sock_release().") Assisted-by: LLM Signed-off-by: Omar Ramadan --- drivers/net/amt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 81d26ef82..3a703b79f 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -3073,9 +3073,14 @@ static int amt_dev_stop(struct net_device *dev) disable_delayed_work_sync(&amt->discovery_wq); cancel_delayed_work_sync(&amt->secret_wq); - /* shutdown */ + /* shutdown. Detach the receive path as well, so that once the + * grace period ends no amt_rcv() can still see the device and queue + * work after the teardown below. + */ sk = rtnl_dereference(amt->sk); RCU_INIT_POINTER(amt->sk, NULL); + if (sk) + rcu_assign_sk_user_data(sk, NULL); synchronize_net(); if (sk) udp_tunnel_sock_release(sk); -- 2.43.0