From: Yuyang Huang Date: Fri, 5 Jun 2026 01:43:18 +0000 (+0900) Subject: ipv4: igmp: annotate data-races around timer-related fields X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3289d17b7a1321e103b8aec4ad82675c03c4764f;p=thirdparty%2Fkernel%2Flinux.git ipv4: igmp: annotate data-races around timer-related fields /proc/net/igmp walks the multicast list locklessly under RCU and reads timer-related fields (im->tm_running, im->reporter, im->timer.expires) to print the timer state of multicast memberships. Concurrently, these fields are modified under im->lock spinlock in timer management paths (igmp_stop_timer(), igmp_start_timer(), and igmp_timer_expire()). Fix this intentional lockless snapshot by annotating the lockless reads with READ_ONCE() and the updates with WRITE_ONCE(). Signed-off-by: Yuyang Huang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260605014318.3890804-3-yuyanghuang@google.com Signed-off-by: Paolo Abeni --- diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index fd0faf042fa67..b6337a47c1418 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -220,8 +220,8 @@ static void igmp_stop_timer(struct ip_mc_list *im) spin_lock_bh(&im->lock); if (timer_delete(&im->timer)) refcount_dec(&im->refcnt); - im->tm_running = 0; - im->reporter = 0; + WRITE_ONCE(im->tm_running, 0); + WRITE_ONCE(im->reporter, 0); im->unsolicit_count = 0; spin_unlock_bh(&im->lock); } @@ -231,7 +231,7 @@ static void igmp_start_timer(struct ip_mc_list *im, int max_delay) { int tv = get_random_u32_below(max_delay); - im->tm_running = 1; + WRITE_ONCE(im->tm_running, 1); if (refcount_inc_not_zero(&im->refcnt)) { if (mod_timer(&im->timer, jiffies + tv + 2)) ip_ma_put(im); @@ -267,7 +267,7 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) if (timer_delete(&im->timer)) { if ((long)(im->timer.expires-jiffies) < max_delay) { add_timer(&im->timer); - im->tm_running = 1; + WRITE_ONCE(im->tm_running, 1); spin_unlock_bh(&im->lock); return; } @@ -857,12 +857,12 @@ static void igmp_timer_expire(struct timer_list *t) struct in_device *in_dev = im->interface; spin_lock(&im->lock); - im->tm_running = 0; + WRITE_ONCE(im->tm_running, 0); if (im->unsolicit_count && --im->unsolicit_count) igmp_start_timer(im, unsolicited_report_interval(in_dev)); - im->reporter = 1; + WRITE_ONCE(im->reporter, 1); spin_unlock(&im->lock); if (IGMP_V1_SEEN(in_dev)) @@ -1325,7 +1325,7 @@ static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) return; - reporter = im->reporter; + reporter = READ_ONCE(im->reporter); igmp_stop_timer(im); if (!in_dev->dead) { @@ -2963,6 +2963,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) struct ip_mc_list *im = v; struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); char *querier; + int tm_running; long delta; #ifdef CONFIG_IP_MULTICAST @@ -2980,13 +2981,14 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) querier); } - delta = im->timer.expires - jiffies; + tm_running = READ_ONCE(im->tm_running); + delta = READ_ONCE(im->timer.expires) - jiffies; seq_printf(seq, "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", im->multiaddr, READ_ONCE(im->users), - im->tm_running, - im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, - im->reporter); + tm_running, + tm_running ? jiffies_delta_to_clock_t(delta) : 0, + READ_ONCE(im->reporter)); } return 0; }