]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ipv4: igmp: annotate data-races around in_dev->mc_count
authorYuyang Huang <yuyanghuang@google.com>
Fri, 5 Jun 2026 01:43:17 +0000 (10:43 +0900)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 9 Jun 2026 11:19:33 +0000 (13:19 +0200)
/proc/net/igmp walks the multicast list for IPv4 interfaces locklessly
under RCU and prints state->in_dev->mc_count. Concurrently, device
init/destruction and multicast join/leave paths update the count
under the RTNL lock. Fix this intentional lockless snapshot by
annotating the read with READ_ONCE() and the updates with WRITE_ONCE().

Signed-off-by: Yuyang Huang <yuyanghuang@google.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Link: https://patch.msgid.link/20260605014318.3890804-2-yuyanghuang@google.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/ipv4/igmp.c

index f2aca659b29c93df3e43a2ee4d34243fcca0b97e..fd0faf042fa67088822863110d026a053c5761d6 100644 (file)
@@ -1566,7 +1566,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
 #endif
 
        im->next_rcu = in_dev->mc_list;
-       in_dev->mc_count++;
+       WRITE_ONCE(in_dev->mc_count, in_dev->mc_count + 1);
        rcu_assign_pointer(in_dev->mc_list, im);
 
        ip_mc_hash_add(in_dev, im);
@@ -1790,7 +1790,8 @@ void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp)
                        if (new_users == 0) {
                                ip_mc_hash_remove(in_dev, i);
                                *ip = i->next_rcu;
-                               in_dev->mc_count--;
+                               WRITE_ONCE(in_dev->mc_count,
+                                          in_dev->mc_count - 1);
                                __igmp_group_dropped(i, gfp);
                                inet_ifmcaddr_notify(in_dev->dev, i,
                                                     RTM_DELMULTICAST);
@@ -1922,7 +1923,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev)
 
        while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) {
                in_dev->mc_list = i->next_rcu;
-               in_dev->mc_count--;
+               WRITE_ONCE(in_dev->mc_count, in_dev->mc_count - 1);
                ip_mc_clear_src(i);
                ip_ma_put(i);
        }
@@ -2974,7 +2975,9 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v)
 
                if (rcu_access_pointer(state->in_dev->mc_list) == im) {
                        seq_printf(seq, "%d\t%-10s: %5d %7s\n",
-                                  state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier);
+                                  state->dev->ifindex, state->dev->name,
+                                  READ_ONCE(state->in_dev->mc_count),
+                                  querier);
                }
 
                delta = im->timer.expires - jiffies;