Netdev List
 help / color / mirror / Atom feed
* [PATCH net] ipv6: mld: use ma_put() after canceling mca delayed work
@ 2026-07-27  9:41 Minhong He
  2026-07-27 12:54 ` Ido Schimmel
  0 siblings, 1 reply; 2+ messages in thread
From: Minhong He @ 2026-07-27  9:41 UTC (permalink / raw)
  To: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ap420073
  Cc: netdev, linux-kernel, Minhong He

When cancel_delayed_work() succeeds, MLD drops the work reference with
refcount_dec(). Unlike ma_put(), that does not free the ifmcaddr6 when
the count reaches zero.

Use ma_put() on stop paths. On restart paths, re-arm the delayed work
before dropping the old reference, matching the ordering used for IPv4
IGMP timer updates.

Similar to commit 3546deaa0c30 ("ipv4: igmp: Fix potential memory leaks
in igmp_mod_timer() and igmp_stop_timer()").

Fixes: 2d9a93b4902b ("mld: convert from timer to delayed work")
Signed-off-by: Minhong He <heminhong@kylinos.cn>
---
 net/ipv6/mcast.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index aaba4c2aae23..32b194c97236 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -76,6 +76,7 @@ static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;
 static void igmp6_join_group(struct ifmcaddr6 *ma);
 static void igmp6_leave_group(struct ifmcaddr6 *ma);
 static void mld_mca_work(struct work_struct *work);
+static void ma_put(struct ifmcaddr6 *mc);
 
 static void mld_ifc_event(struct inet6_dev *idev);
 static bool mld_in_v1_mode(const struct inet6_dev *idev);
@@ -724,7 +725,7 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc)
 		igmp6_leave_group(mc);
 
 	if (cancel_delayed_work(&mc->mca_work))
-		refcount_dec(&mc->mca_refcnt);
+		ma_put(mc);
 }
 
 /* deleted ifmcaddr6 manipulation */
@@ -1156,6 +1157,7 @@ static void mld_report_stop_work(struct inet6_dev *idev)
 static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
 {
 	unsigned long delay = resptime;
+	bool put = false;
 
 	mc_assert_locked(ma->idev);
 
@@ -1165,7 +1167,7 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
 		return;
 
 	if (cancel_delayed_work(&ma->mca_work)) {
-		refcount_dec(&ma->mca_refcnt);
+		put = true;
 		delay = ma->mca_work.timer.expires - jiffies;
 	}
 
@@ -1175,6 +1177,9 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
 	if (!mod_delayed_work(mld_wq, &ma->mca_work, delay))
 		refcount_inc(&ma->mca_refcnt);
 	WRITE_ONCE(ma->mca_flags, ma->mca_flags | MAF_TIMER_RUNNING);
+
+	if (put)
+		ma_put(ma);
 }
 
 /* mark EXCLUDE-mode sources */
@@ -1641,7 +1646,7 @@ static void __mld_report_work(struct sk_buff *skb)
 	for_each_mc_mclock(idev, ma) {
 		if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) {
 			if (cancel_delayed_work(&ma->mca_work))
-				refcount_dec(&ma->mca_refcnt);
+				ma_put(ma);
 			WRITE_ONCE(ma->mca_flags,
 				   ma->mca_flags & ~(MAF_LAST_REPORTER |
 						     MAF_TIMER_RUNNING));
@@ -2622,6 +2627,7 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)
 static void igmp6_join_group(struct ifmcaddr6 *ma)
 {
 	unsigned long delay;
+	bool put = false;
 
 	mc_assert_locked(ma->idev);
 
@@ -2633,7 +2639,7 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
 	delay = get_random_u32_below(unsolicited_report_interval(ma->idev));
 
 	if (cancel_delayed_work(&ma->mca_work)) {
-		refcount_dec(&ma->mca_refcnt);
+		put = true;
 		delay = ma->mca_work.timer.expires - jiffies;
 	}
 
@@ -2641,6 +2647,9 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
 		refcount_inc(&ma->mca_refcnt);
 	WRITE_ONCE(ma->mca_flags, ma->mca_flags |
 		   MAF_TIMER_RUNNING | MAF_LAST_REPORTER);
+
+	if (put)
+		ma_put(ma);
 }
 
 static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH net] ipv6: mld: use ma_put() after canceling mca delayed work
  2026-07-27  9:41 [PATCH net] ipv6: mld: use ma_put() after canceling mca delayed work Minhong He
@ 2026-07-27 12:54 ` Ido Schimmel
  0 siblings, 0 replies; 2+ messages in thread
From: Ido Schimmel @ 2026-07-27 12:54 UTC (permalink / raw)
  To: Minhong He
  Cc: dsahern, davem, edumazet, kuba, pabeni, horms, ap420073, netdev,
	linux-kernel

On Mon, Jul 27, 2026 at 05:41:07PM +0800, Minhong He wrote:
> When cancel_delayed_work() succeeds, MLD drops the work reference with
> refcount_dec(). Unlike ma_put(), that does not free the ifmcaddr6 when
> the count reaches zero.
> 
> Use ma_put() on stop paths. On restart paths, re-arm the delayed work
> before dropping the old reference, matching the ordering used for IPv4
> IGMP timer updates.
> 
> Similar to commit 3546deaa0c30 ("ipv4: igmp: Fix potential memory leaks
> in igmp_mod_timer() and igmp_stop_timer()").

In IPv4 the readers grab the multicast group from in_dev->mc_list using
RCU so they could race with the removal of the group from the list. In
IPv6, AFAICT, the readers iterate over the list using the idev->mc_lock
mutex, so I don't see how the same race can happen there.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-27 12:54 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-27  9:41 [PATCH net] ipv6: mld: use ma_put() after canceling mca delayed work Minhong He
2026-07-27 12:54 ` Ido Schimmel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox