From: Eric Dumazet <edumazet@google.com> mainline inclusion from mainline-v7.2-rc3 commit 9b26518b6896a16b809b1e42986f4ebac7bccc1e category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/17356 CVE: CVE-2026-72322 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- A race condition exists between device teardown and incoming MLD query processing, leading to a Use-After-Free in the MLD delayed work. During device destruction, the primary reference to inet6_dev is dropped, which can drop its refcount to 0. The actual freeing of inet6_dev memory is deferred via RCU. Concurrently, the packet receive path runs under RCU read lock and obtains the inet6_dev pointer. Because the memory is RCU-protected, CPU-0 can safely dereference inet6_dev even if its refcount has hit 0. However, if CPU-0 calls igmp6_event_query() and schedules delayed work, it attempts to acquire a reference using in6_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the inet6_dev memory is still scheduled to be freed after the RCU grace period, the device is freed while the work is still scheduled. When the work runs, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in6_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not schedule the work. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Link: https://patch.msgid.link/20260705181756.963063-3-edumazet@google.com Signed-off-by: Paolo Abeni <pabeni@redhat.com> Conflicts: include/net/addrconf.h net/ipv6/mcast.c [Context differences are due to the following patches not being merged: 2d9a93b4902b ("mld: convert from timer to delayed work") f185de28d9ae ("mld: add new workqueues for process mld events") 81895a65ec63 ("treewide: use prandom_u32_max() when possible, part 1") 8032bf1233a7 ("treewide: use get_random_u32_below() instead of deprecated function")] Signed-off-by: JiangJieHua <jiangjiehua1@huawei.com> --- include/net/addrconf.h | 5 +++++ net/ipv6/mcast.c | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 10d270f004f0d..26f0a5bc234be 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -425,6 +425,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev) refcount_inc(&idev->refcnt); } +static inline bool in6_dev_hold_safe(struct inet6_dev *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp); static inline void in6_ifa_put(struct inet6_ifaddr *ifp) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index bb484fe27e137..787800af097a5 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1037,8 +1037,10 @@ static void mld_gq_start_timer(struct inet6_dev *idev) unsigned long tv = prandom_u32() % idev->mc_maxdelay; idev->mc_gq_running = 1; - if (!mod_timer(&idev->mc_gq_timer, jiffies+tv+2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_timer(&idev->mc_gq_timer, jiffies+tv+2)) + in6_dev_put(idev); + } } static void mld_gq_stop_timer(struct inet6_dev *idev) @@ -1052,8 +1054,10 @@ static void mld_ifc_start_timer(struct inet6_dev *idev, unsigned long delay) { unsigned long tv = prandom_u32() % delay; - if (!mod_timer(&idev->mc_ifc_timer, jiffies+tv+2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_timer(&idev->mc_ifc_timer, jiffies+tv+2)) + in6_dev_put(idev); + } } static void mld_ifc_stop_timer(struct inet6_dev *idev) @@ -1067,8 +1071,10 @@ static void mld_dad_start_timer(struct inet6_dev *idev, unsigned long delay) { unsigned long tv = prandom_u32() % delay; - if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_timer(&idev->mc_dad_timer, jiffies+tv+2)) + in6_dev_put(idev); + } } static void mld_dad_stop_timer(struct inet6_dev *idev) -- 2.33.8