Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next v2 0/2] ipv6: mcast: annotate data races in /proc/net/igmp6
@ 2026-06-09  8:11 Yuyang Huang
  2026-06-09  8:11 ` [PATCH net-next v2 1/2] ipv6: mcast: annotate data-races around mca_flags Yuyang Huang
  2026-06-09  8:11 ` [PATCH net-next v2 2/2] ipv6: mcast: annotate igmp6 timer expiry race Yuyang Huang
  0 siblings, 2 replies; 5+ messages in thread
From: Yuyang Huang @ 2026-06-09  8:11 UTC (permalink / raw)
  To: Yuyang Huang
  Cc: David S. Miller, David Ahern, Eric Dumazet, Ido Schimmel,
	Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, netdev

/proc/net/igmp6 walks IPv6 multicast memberships under RCU without
holding idev->mc_lock, taking a lockless snapshot of two fields that
writers update under the lock: mca_flags and mca_work.timer.expires.

Patch 1 adds WRITE_ONCE() to all mca_flags update sites and READ_ONCE()
to the procfs reader.  Patch 2 does the same for the timer.expires read
in the procfs path.

Changes in v2:
- patch 2: read mca_flags before timer.expires to match write-side
  ordering and avoid printing a garbage timer value.

Yuyang Huang (2):
  ipv6: mcast: annotate data-races around mca_flags
  ipv6: mcast: annotate igmp6 timer expiry race

 net/ipv6/mcast.c | 55 ++++++++++++++++++++++++++++++------------------
 1 file changed, 34 insertions(+), 21 deletions(-)

-- 
2.43.0


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

* [PATCH net-next v2 1/2] ipv6: mcast: annotate data-races around mca_flags
  2026-06-09  8:11 [PATCH net-next v2 0/2] ipv6: mcast: annotate data races in /proc/net/igmp6 Yuyang Huang
@ 2026-06-09  8:11 ` Yuyang Huang
  2026-06-09  9:36   ` Eric Dumazet
  2026-06-09  8:11 ` [PATCH net-next v2 2/2] ipv6: mcast: annotate igmp6 timer expiry race Yuyang Huang
  1 sibling, 1 reply; 5+ messages in thread
From: Yuyang Huang @ 2026-06-09  8:11 UTC (permalink / raw)
  To: Yuyang Huang
  Cc: David S. Miller, David Ahern, Eric Dumazet, Ido Schimmel,
	Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, netdev

/proc/net/igmp6 walks IPv6 multicast memberships under RCU and
prints mca_flags without holding idev->mc_lock. The multicast paths
update the field while holding idev->mc_lock.

Annotate this intentional lockless snapshot with READ_ONCE() and the
matching writers with WRITE_ONCE().

Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
---
 net/ipv6/mcast.c | 52 +++++++++++++++++++++++++++++-------------------
 1 file changed, 32 insertions(+), 20 deletions(-)

diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index b8901c921c5e..bd3972730aa0 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -676,7 +676,7 @@ static void igmp6_group_added(struct ifmcaddr6 *mc)
 		return;
 
 	if (!(mc->mca_flags&MAF_LOADED)) {
-		mc->mca_flags |= MAF_LOADED;
+		WRITE_ONCE(mc->mca_flags, mc->mca_flags | MAF_LOADED);
 		if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0)
 			dev_mc_add(dev, buf);
 	}
@@ -712,7 +712,7 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc)
 		return;
 
 	if (mc->mca_flags&MAF_LOADED) {
-		mc->mca_flags &= ~MAF_LOADED;
+		WRITE_ONCE(mc->mca_flags, mc->mca_flags & ~MAF_LOADED);
 		if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0)
 			dev_mc_del(dev, buf);
 	}
@@ -886,7 +886,7 @@ static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
 
 	if (ipv6_addr_is_ll_all_nodes(&mc->mca_addr) ||
 	    IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL)
-		mc->mca_flags |= MAF_NOREPORT;
+		WRITE_ONCE(mc->mca_flags, mc->mca_flags | MAF_NOREPORT);
 
 	return mc;
 }
@@ -1167,7 +1167,7 @@ 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);
-	ma->mca_flags |= MAF_TIMER_RUNNING;
+	WRITE_ONCE(ma->mca_flags, ma->mca_flags | MAF_TIMER_RUNNING);
 }
 
 /* mark EXCLUDE-mode sources */
@@ -1195,7 +1195,7 @@ static bool mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs,
 			}
 		}
 	}
-	pmc->mca_flags &= ~MAF_GSQUERY;
+	WRITE_ONCE(pmc->mca_flags, pmc->mca_flags & ~MAF_GSQUERY);
 	if (scount == nsrcs)	/* all sources excluded */
 		return false;
 	return true;
@@ -1227,10 +1227,10 @@ static bool mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,
 		}
 	}
 	if (!scount) {
-		pmc->mca_flags &= ~MAF_GSQUERY;
+		WRITE_ONCE(pmc->mca_flags, pmc->mca_flags & ~MAF_GSQUERY);
 		return false;
 	}
-	pmc->mca_flags |= MAF_GSQUERY;
+	WRITE_ONCE(pmc->mca_flags, pmc->mca_flags | MAF_GSQUERY);
 	return true;
 }
 
@@ -1499,18 +1499,25 @@ static void __mld_query_work(struct sk_buff *skb)
 		}
 	} else {
 		for_each_mc_mclock(idev, ma) {
+			unsigned int flags;
+
 			if (!ipv6_addr_equal(&group, &ma->mca_addr))
 				continue;
-			if (ma->mca_flags & MAF_TIMER_RUNNING) {
+			flags = ma->mca_flags;
+
+			if (flags & MAF_TIMER_RUNNING) {
 				/* gsquery <- gsquery && mark */
 				if (!mark)
-					ma->mca_flags &= ~MAF_GSQUERY;
+					WRITE_ONCE(ma->mca_flags,
+						   flags & ~MAF_GSQUERY);
 			} else {
 				/* gsquery <- mark */
 				if (mark)
-					ma->mca_flags |= MAF_GSQUERY;
+					WRITE_ONCE(ma->mca_flags,
+						   flags | MAF_GSQUERY);
 				else
-					ma->mca_flags &= ~MAF_GSQUERY;
+					WRITE_ONCE(ma->mca_flags,
+						   flags & ~MAF_GSQUERY);
 			}
 			if (!(ma->mca_flags & MAF_GSQUERY) ||
 			    mld_marksources(ma, ntohs(mlh2->mld2q_nsrcs), mlh2->mld2q_srcs))
@@ -1618,8 +1625,9 @@ static void __mld_report_work(struct sk_buff *skb)
 		if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) {
 			if (cancel_delayed_work(&ma->mca_work))
 				refcount_dec(&ma->mca_refcnt);
-			ma->mca_flags &= ~(MAF_LAST_REPORTER |
-					   MAF_TIMER_RUNNING);
+			WRITE_ONCE(ma->mca_flags,
+				   ma->mca_flags & ~(MAF_LAST_REPORTER |
+						     MAF_TIMER_RUNNING));
 			break;
 		}
 	}
@@ -2018,8 +2026,10 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
 	if (pgr)
 		pgr->grec_nsrcs = htons(scount);
 
-	if (isquery)
-		pmc->mca_flags &= ~MAF_GSQUERY;	/* clear query state */
+	if (isquery) {
+		/* clear query state */
+		WRITE_ONCE(pmc->mca_flags, pmc->mca_flags & ~MAF_GSQUERY);
+	}
 	return skb;
 }
 
@@ -2612,7 +2622,8 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
 
 	if (!mod_delayed_work(mld_wq, &ma->mca_work, delay))
 		refcount_inc(&ma->mca_refcnt);
-	ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
+	WRITE_ONCE(ma->mca_flags, ma->mca_flags |
+		   MAF_TIMER_RUNNING | MAF_LAST_REPORTER);
 }
 
 static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
@@ -2713,8 +2724,8 @@ static void mld_mca_work(struct work_struct *work)
 		igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
 	else
 		mld_send_report(ma->idev, ma);
-	ma->mca_flags |=  MAF_LAST_REPORTER;
-	ma->mca_flags &= ~MAF_TIMER_RUNNING;
+	WRITE_ONCE(ma->mca_flags, (ma->mca_flags | MAF_LAST_REPORTER) &
+		   ~MAF_TIMER_RUNNING);
 	mutex_unlock(&ma->idev->mc_lock);
 
 	ma_put(ma);
@@ -2972,13 +2983,14 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
 {
 	struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;
 	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+	unsigned int mca_flags = READ_ONCE(im->mca_flags);
 
 	seq_printf(seq,
 		   "%-4d %-15s %pi6 %5d %08X %ld\n",
 		   state->dev->ifindex, state->dev->name,
 		   &im->mca_addr,
-		   READ_ONCE(im->mca_users), im->mca_flags,
-		   (im->mca_flags & MAF_TIMER_RUNNING) ?
+		   READ_ONCE(im->mca_users), mca_flags,
+		   (mca_flags & MAF_TIMER_RUNNING) ?
 		   jiffies_to_clock_t(im->mca_work.timer.expires - jiffies) : 0);
 	return 0;
 }
-- 
2.43.0


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

* [PATCH net-next v2 2/2] ipv6: mcast: annotate igmp6 timer expiry race
  2026-06-09  8:11 [PATCH net-next v2 0/2] ipv6: mcast: annotate data races in /proc/net/igmp6 Yuyang Huang
  2026-06-09  8:11 ` [PATCH net-next v2 1/2] ipv6: mcast: annotate data-races around mca_flags Yuyang Huang
@ 2026-06-09  8:11 ` Yuyang Huang
  1 sibling, 0 replies; 5+ messages in thread
From: Yuyang Huang @ 2026-06-09  8:11 UTC (permalink / raw)
  To: Yuyang Huang
  Cc: David S. Miller, David Ahern, Eric Dumazet, Ido Schimmel,
	Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, netdev

/proc/net/igmp6 walks IPv6 multicast memberships under RCU and reads
mca_work.timer.expires to print the remaining multicast timer. The
delayed-work timer can be updated concurrently.

Annotate the intentional lockless procfs snapshot with READ_ONCE().

Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
---
 net/ipv6/mcast.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index bd3972730aa0..04b811b3be97 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -2984,6 +2984,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
 	struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;
 	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
 	unsigned int mca_flags = READ_ONCE(im->mca_flags);
+	unsigned long expires = READ_ONCE(im->mca_work.timer.expires);
 
 	seq_printf(seq,
 		   "%-4d %-15s %pi6 %5d %08X %ld\n",
@@ -2991,7 +2992,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
 		   &im->mca_addr,
 		   READ_ONCE(im->mca_users), mca_flags,
 		   (mca_flags & MAF_TIMER_RUNNING) ?
-		   jiffies_to_clock_t(im->mca_work.timer.expires - jiffies) : 0);
+		   jiffies_to_clock_t(expires - jiffies) : 0);
 	return 0;
 }
 
-- 
2.43.0


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

* Re: [PATCH net-next v2 1/2] ipv6: mcast: annotate data-races around mca_flags
  2026-06-09  8:11 ` [PATCH net-next v2 1/2] ipv6: mcast: annotate data-races around mca_flags Yuyang Huang
@ 2026-06-09  9:36   ` Eric Dumazet
  2026-06-09 10:39     ` Yuyang Huang
  0 siblings, 1 reply; 5+ messages in thread
From: Eric Dumazet @ 2026-06-09  9:36 UTC (permalink / raw)
  To: Yuyang Huang
  Cc: David S. Miller, David Ahern, Ido Schimmel, Jakub Kicinski,
	Paolo Abeni, Simon Horman, linux-kernel, netdev

On Tue, Jun 9, 2026 at 1:11 AM Yuyang Huang <sigefriedhyy@gmail.com> wrote:
>
> /proc/net/igmp6 walks IPv6 multicast memberships under RCU and
> prints mca_flags without holding idev->mc_lock. The multicast paths
> update the field while holding idev->mc_lock.
>
> Annotate this intentional lockless snapshot with READ_ONCE() and the
> matching writers with WRITE_ONCE().
>
> Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
> ---

You forgot to carry Ido's tag? Have you changed something in this
patch compared to V1?

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

* Re: [PATCH net-next v2 1/2] ipv6: mcast: annotate data-races around mca_flags
  2026-06-09  9:36   ` Eric Dumazet
@ 2026-06-09 10:39     ` Yuyang Huang
  0 siblings, 0 replies; 5+ messages in thread
From: Yuyang Huang @ 2026-06-09 10:39 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S. Miller, David Ahern, Ido Schimmel, Jakub Kicinski,
	Paolo Abeni, Simon Horman, linux-kernel, netdev

On Tue, Jun 9, 2026 at 6:36 PM Eric Dumazet <edumazet@google.com> wrote:
>
> On Tue, Jun 9, 2026 at 1:11 AM Yuyang Huang <sigefriedhyy@gmail.com> wrote:
> >
> > /proc/net/igmp6 walks IPv6 multicast memberships under RCU and
> > prints mca_flags without holding idev->mc_lock. The multicast paths
> > update the field while holding idev->mc_lock.
> >
> > Annotate this intentional lockless snapshot with READ_ONCE() and the
> > matching writers with WRITE_ONCE().
> >
> > Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
> > ---
>
> You forgot to carry Ido's tag? Have you changed something in this
> patch compared to V1?

Sorry, I forgot to carry over Ido's Reviewed-by tag for patch 1. I
didn't touch patch 1, as shown in the cover letter, I only touched
patch 2 to fix the bug pointed out by Sashiko.
Shall I resent the V2 patch sets to add back the tag?

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

end of thread, other threads:[~2026-06-09 10:39 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-09  8:11 [PATCH net-next v2 0/2] ipv6: mcast: annotate data races in /proc/net/igmp6 Yuyang Huang
2026-06-09  8:11 ` [PATCH net-next v2 1/2] ipv6: mcast: annotate data-races around mca_flags Yuyang Huang
2026-06-09  9:36   ` Eric Dumazet
2026-06-09 10:39     ` Yuyang Huang
2026-06-09  8:11 ` [PATCH net-next v2 2/2] ipv6: mcast: annotate igmp6 timer expiry race Yuyang Huang

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