[PATCH openEuler-1.0-LTS] ipv6: mcast: Fix potential UAF in MLD delayed work
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 [addrconf.h: the upstream hunk context after in6_dev_hold() references ip6_ignore_linkdown(), which does not exist in 4.19, so only the new in6_dev_hold_safe() helper is added and the ip6_ignore_linkdown() context is dropped. mcast.c: 4.19 use timers (mld_gq_start_timer/mld_ifc_start_timer/ mld_dad_start_timer + mod_timer) instead of workqueue (mld_*_start_work + mod_delayed_work); igmp6_event_query/report have no workqueue queues, so those hunks are not applicable. The refcount fix is applied to the three _start_timer functions instead, preserving the upstream semantics.In 4.19, the `igmp6_event_query/report` hunks are dropped entirely (sync implementation, no workqueue queues); the target's existing refcount sites are already covered by the three `_start_timer` fixes.] 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
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,转换为PR失败! 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/7GD... 失败原因:调用atomgit api创建PR失败, 失败原因如下: Backend timeout 建议解决方法:请稍等,机器人会在下一次任务重新执行 FeedBack: The patch(es) which you have sent to kernel@openeuler.org has been converted to PR failed! Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/7GD... Failed Reason: create PR failed when call atomgit's api, failed reason is as follows: Backend timeout Suggest Solution: please wait, the bot will retry in the next interval
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/26938 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/7GD... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/26938 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/7GD...
participants (2)
-
JiangJieHua -
patchwork bot