* [PATCH net] igmp: convert struct ip_sf_list to RCU
@ 2026-08-26 11:59 Eric Dumazet
2026-08-27 8:13 ` kernel test robot
0 siblings, 1 reply; 3+ messages in thread
From: Eric Dumazet @ 2026-08-26 11:59 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, netdev, eric.dumazet, Eric Dumazet,
syzbot+a5a07d2f6a71972dc5ed
Commit 23d2b94043ca ("igmp: Add ip_mc_list lock in ip_check_mc_rcu")
added spin_lock_bh(&im->lock) to ip_check_mc_rcu() to prevent a
use-after-free while iterating im->sources during concurrent deletions.
However, ip_check_mc_rcu() is called from RCU read-side critical
sections in packet receive and route lookup fast paths (e.g.
__mkroute_output(), ip_route_input_rcu(), and __udp4_lib_rcv()).
When igmpv3_send_cr() or igmpv3_send_report() holds &pmc->lock and
calls add_grec() -> igmpv3_newpack() -> ip_route_output_ports(),
an XFRM policy matching a multicast destination triggers
xfrm_tmpl_resolve_one() -> xfrm4_get_saddr() -> __mkroute_output() ->
ip_check_mc_rcu(). This attempts to acquire &im->lock while &pmc->lock
is already held on the same CPU, triggering a lockdep recursive locking
warning / deadlock.
Fix this by converting IPv4 struct ip_sf_list to RCU, mirroring the
IPv6 implementation in net/ipv6/mcast.c:
1. Add struct rcu_head to struct ip_sf_list and annotate sf_next,
sources, and tomb as __rcu pointers.
2. Use rcu_assign_pointer() and kfree_rcu() for list updates and
deletions.
3. Remove spin_lock_bh(&im->lock) from ip_check_mc_rcu() and traverse
im->sources locklessly with for_each_psf_rcu(), reading and writing
counter fields with READ_ONCE() and WRITE_ONCE().
Note: RCU conversion of /proc/net/mcfilter will be done in a
separate patch.
Fixes: 23d2b94043ca ("igmp: Add ip_mc_list lock in ip_check_mc_rcu")
Reported-by: syzbot+a5a07d2f6a71972dc5ed@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/6a8ed434.1d9ded08.62e62.00a9.GAE@google.com/T/#u
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/linux/igmp.h | 7 +-
net/ipv4/igmp.c | 204 +++++++++++++++++++++++++++----------------
2 files changed, 132 insertions(+), 79 deletions(-)
diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 3a2d35a9f3078f12213f49cba90bd59582c134fc..a0cf0398519fd7fc8b05bc625a7f099a1ed6b82a 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -57,20 +57,21 @@ struct ip_mc_socklist {
};
struct ip_sf_list {
- struct ip_sf_list *sf_next;
+ struct ip_sf_list __rcu *sf_next;
unsigned long sf_count[2]; /* include/exclude counts */
__be32 sf_inaddr;
unsigned char sf_gsresp; /* include in g & s response? */
unsigned char sf_oldin; /* change state */
unsigned char sf_crcount; /* retrans. left to send */
+ struct rcu_head rcu;
};
struct ip_mc_list {
struct in_device *interface;
__be32 multiaddr;
unsigned int sfmode;
- struct ip_sf_list *sources;
- struct ip_sf_list *tomb;
+ struct ip_sf_list __rcu *sources;
+ struct ip_sf_list __rcu *tomb;
unsigned long sfcount[2];
union {
struct ip_mc_list *next;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index b80b8a92f46e5f3dc4b9f906bec0a4035e4fbf70..5516329c9caa5f6f3ec7a2f633bda5d1a16eb4ec 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -188,6 +188,10 @@ static void ip_ma_put(struct ip_mc_list *im)
}
}
+#define pmc_dereference(e, pmc) \
+ rcu_dereference_protected(e, lockdep_is_held(&(pmc)->lock) || \
+ lockdep_is_held(&(pmc)->interface->mc_tomb_lock))
+
#define for_each_pmc_rcu(in_dev, pmc) \
for (pmc = rcu_dereference(in_dev->mc_list); \
pmc != NULL; \
@@ -198,13 +202,28 @@ static void ip_ma_put(struct ip_mc_list *im)
pmc != NULL; \
pmc = rtnl_dereference(pmc->next_rcu))
+#define for_each_psf_mclock(pmc, psf) \
+ for (psf = pmc_dereference((pmc)->sources, pmc); \
+ psf; \
+ psf = pmc_dereference(psf->sf_next, pmc))
+
+#define for_each_psf_rcu(im, psf) \
+ for (psf = rcu_dereference((im)->sources); \
+ psf; \
+ psf = rcu_dereference(psf->sf_next))
+
+#define for_each_psf_tomb(pmc, psf) \
+ for (psf = pmc_dereference((pmc)->tomb, pmc); \
+ psf; \
+ psf = pmc_dereference(psf->sf_next, pmc))
+
static void ip_sf_list_clear_all(struct ip_sf_list *psf)
{
struct ip_sf_list *next;
while (psf) {
- next = psf->sf_next;
- kfree(psf);
+ next = rcu_dereference_protected(psf->sf_next, 1);
+ kfree_rcu(psf, rcu);
psf = next;
}
}
@@ -349,7 +368,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
struct ip_sf_list *psf;
int scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (!is_in(pmc, psf, type, gdeleted, sdeleted))
continue;
scount++;
@@ -494,7 +513,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
struct net *net = dev_net(dev);
struct igmpv3_report *pih;
struct igmpv3_grec *pgr = NULL;
- struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
+ struct ip_sf_list *psf, *psf_next, *psf_prev;
+ struct ip_sf_list __rcu **psf_list;
int scount, stotal, first, isquery, truncate;
unsigned int mtu;
@@ -517,7 +537,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
- if (!*psf_list)
+ if (!rcu_access_pointer(*psf_list))
goto empty_source;
pih = skb ? igmpv3_report_hdr(skb) : NULL;
@@ -533,10 +553,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
}
first = 1;
psf_prev = NULL;
- for (psf = *psf_list; psf; psf = psf_next) {
+ for (psf = pmc_dereference(*psf_list, pmc);
+ psf;
+ psf = psf_next) {
__be32 *psrc;
- psf_next = psf->sf_next;
+ psf_next = pmc_dereference(psf->sf_next, pmc);
if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
psf_prev = psf;
@@ -583,10 +605,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
psf->sf_crcount--;
if ((sdeleted || gdeleted) && psf->sf_crcount == 0) {
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next,
+ psf_next);
else
- *psf_list = psf->sf_next;
- kfree(psf);
+ rcu_assign_pointer(*psf_list,
+ psf_next);
+ kfree_rcu(psf, rcu);
continue;
}
}
@@ -655,28 +679,29 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
/*
* remove zero-count source records from a source filter list
*/
-static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
+static void igmpv3_clear_zeros(struct ip_sf_list __rcu **ppsf)
{
struct ip_sf_list *psf_prev, *psf_next, *psf;
psf_prev = NULL;
- for (psf = *ppsf; psf; psf = psf_next) {
- psf_next = psf->sf_next;
+ for (psf = rcu_dereference_protected(*ppsf, 1); psf; psf = psf_next) {
+ psf_next = rcu_dereference_protected(psf->sf_next, 1);
if (psf->sf_crcount == 0) {
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next, psf_next);
else
- *ppsf = psf->sf_next;
- kfree(psf);
- } else
+ rcu_assign_pointer(*ppsf, psf_next);
+ kfree_rcu(psf, rcu);
+ } else {
psf_prev = psf;
+ }
}
}
static void kfree_pmc(struct ip_mc_list *pmc)
{
- ip_sf_list_clear_all(pmc->sources);
- ip_sf_list_clear_all(pmc->tomb);
+ ip_sf_list_clear_all(rcu_dereference_protected(pmc->sources, 1));
+ ip_sf_list_clear_all(rcu_dereference_protected(pmc->tomb, 1));
kfree(pmc);
}
@@ -710,7 +735,8 @@ static void igmpv3_send_cr(struct in_device *in_dev)
igmpv3_clear_zeros(&pmc->sources);
}
}
- if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) {
+ if (pmc->crcount == 0 && !rcu_access_pointer(pmc->tomb) &&
+ !rcu_access_pointer(pmc->sources)) {
if (pmc_prev)
pmc_prev->next = pmc_next;
else
@@ -896,7 +922,7 @@ static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
int i, scount;
scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
break;
for (i = 0; i < nsrcs; i++) {
@@ -927,7 +953,7 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
/* mark INCLUDE-mode sources */
scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
break;
for (i = 0; i < nsrcs; i++)
@@ -1228,11 +1254,12 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
if (pmc->sfmode == MCAST_INCLUDE) {
struct ip_sf_list *psf;
+ for_each_psf_mclock(im, psf)
+ psf->sf_crcount = pmc->crcount;
pmc->tomb = im->tomb;
pmc->sources = im->sources;
- im->tomb = im->sources = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next)
- psf->sf_crcount = pmc->crcount;
+ RCU_INIT_POINTER(im->tomb, NULL);
+ RCU_INIT_POINTER(im->sources, NULL);
}
spin_unlock_bh(&im->lock);
@@ -1271,9 +1298,18 @@ static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im)
if (pmc) {
im->interface = pmc->interface;
if (im->sfmode == MCAST_INCLUDE) {
- swap(im->tomb, pmc->tomb);
- swap(im->sources, pmc->sources);
- for (psf = im->sources; psf; psf = psf->sf_next)
+ struct ip_sf_list *sources, *tomb;
+
+ tomb = rcu_replace_pointer(im->tomb,
+ rcu_dereference_protected(pmc->tomb, 1),
+ lockdep_is_held(&im->lock));
+ rcu_assign_pointer(pmc->tomb, tomb);
+
+ sources = rcu_replace_pointer(im->sources,
+ rcu_dereference_protected(pmc->sources, 1),
+ lockdep_is_held(&im->lock));
+ rcu_assign_pointer(pmc->sources, sources);
+ for_each_psf_mclock(im, psf)
psf->sf_crcount = in_dev->mr_qrv ?:
READ_ONCE(net->ipv4.sysctl_igmp_qrv);
} else {
@@ -1310,8 +1346,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
struct ip_sf_list *psf;
spin_lock_bh(&pmc->lock);
- psf = pmc->tomb;
- pmc->tomb = NULL;
+ psf = pmc_dereference(pmc->tomb, pmc);
+ RCU_INIT_POINTER(pmc->tomb, NULL);
spin_unlock_bh(&pmc->lock);
ip_sf_list_clear_all(psf);
}
@@ -1990,7 +2026,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
int rv = 0;
psf_prev = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
@@ -1999,7 +2035,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
/* source filter not found, or count wrong => bug */
return -ESRCH;
}
- psf->sf_count[sfmode]--;
+ WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] - 1);
if (psf->sf_count[sfmode] == 0) {
ip_rt_multicast_event(pmc->interface);
}
@@ -2011,19 +2047,28 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
/* no more filters for this source */
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next,
+ pmc_dereference(psf->sf_next, pmc));
else
- pmc->sources = psf->sf_next;
+ rcu_assign_pointer(pmc->sources,
+ pmc_dereference(psf->sf_next, pmc));
#ifdef CONFIG_IP_MULTICAST
if (psf->sf_oldin &&
!IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) {
- psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
- psf->sf_next = pmc->tomb;
- pmc->tomb = psf;
- rv = 1;
- } else
+ struct ip_sf_list *dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
+
+ if (dpsf) {
+ *dpsf = *psf;
+ dpsf->sf_crcount = in_dev->mr_qrv ?:
+ READ_ONCE(net->ipv4.sysctl_igmp_qrv);
+ rcu_assign_pointer(dpsf->sf_next,
+ pmc_dereference(pmc->tomb, pmc));
+ rcu_assign_pointer(pmc->tomb, dpsf);
+ rv = 1;
+ }
+ }
#endif
- kfree(psf);
+ kfree_rcu(psf, rcu);
}
return rv;
}
@@ -2060,7 +2105,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
err = -EINVAL;
if (!pmc->sfcount[sfmode])
goto out_unlock;
- pmc->sfcount[sfmode]--;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
}
err = 0;
for (i = 0; i < sfcount; i++) {
@@ -2083,7 +2128,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#ifdef CONFIG_IP_MULTICAST
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf)
psf->sf_crcount = 0;
igmp_ifc_event(pmc->interface);
} else if (sf_setstate(pmc) || changerec) {
@@ -2104,7 +2149,7 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
struct ip_sf_list *psf, *psf_prev;
psf_prev = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
@@ -2114,12 +2159,12 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
if (!psf)
return -ENOBUFS;
psf->sf_inaddr = *psfsrc;
- if (psf_prev) {
- psf_prev->sf_next = psf;
- } else
- pmc->sources = psf;
+ if (psf_prev)
+ rcu_assign_pointer(psf_prev->sf_next, psf);
+ else
+ rcu_assign_pointer(pmc->sources, psf);
}
- psf->sf_count[sfmode]++;
+ WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] + 1);
if (psf->sf_count[sfmode] == 1) {
ip_rt_multicast_event(pmc->interface);
}
@@ -2132,13 +2177,15 @@ static void sf_markstate(struct ip_mc_list *pmc)
struct ip_sf_list *psf;
int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf) {
if (pmc->sfcount[MCAST_EXCLUDE]) {
psf->sf_oldin = mca_xcount ==
psf->sf_count[MCAST_EXCLUDE] &&
!psf->sf_count[MCAST_INCLUDE];
- } else
+ } else {
psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0;
+ }
+ }
}
static int sf_setstate(struct ip_mc_list *pmc)
@@ -2149,27 +2196,31 @@ static int sf_setstate(struct ip_mc_list *pmc)
int new_in, rv;
rv = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (pmc->sfcount[MCAST_EXCLUDE]) {
new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] &&
!psf->sf_count[MCAST_INCLUDE];
- } else
+ } else {
new_in = psf->sf_count[MCAST_INCLUDE] != 0;
+ }
if (new_in) {
if (!psf->sf_oldin) {
struct ip_sf_list *prev = NULL;
- for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) {
+ for_each_psf_tomb(pmc, dpsf) {
if (dpsf->sf_inaddr == psf->sf_inaddr)
break;
prev = dpsf;
}
if (dpsf) {
+ struct ip_sf_list *dpsf_next;
+
+ dpsf_next = pmc_dereference(dpsf->sf_next, pmc);
if (prev)
- prev->sf_next = dpsf->sf_next;
+ rcu_assign_pointer(prev->sf_next, dpsf_next);
else
- pmc->tomb = dpsf->sf_next;
- kfree(dpsf);
+ rcu_assign_pointer(pmc->tomb, dpsf_next);
+ kfree_rcu(dpsf, rcu);
}
psf->sf_crcount = qrv;
rv++;
@@ -2181,17 +2232,19 @@ static int sf_setstate(struct ip_mc_list *pmc)
* add or update "delete" records if an active filter
* is now inactive
*/
- for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next)
+ for_each_psf_tomb(pmc, dpsf) {
if (dpsf->sf_inaddr == psf->sf_inaddr)
break;
+ }
if (!dpsf) {
dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
if (!dpsf)
continue;
*dpsf = *psf;
/* pmc->lock held by callers */
- dpsf->sf_next = pmc->tomb;
- pmc->tomb = dpsf;
+ rcu_assign_pointer(dpsf->sf_next,
+ pmc_dereference(pmc->tomb, pmc));
+ rcu_assign_pointer(pmc->tomb, dpsf);
}
dpsf->sf_crcount = qrv;
rv++;
@@ -2231,7 +2284,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#endif
isexclude = pmc->sfmode == MCAST_EXCLUDE;
if (!delta)
- pmc->sfcount[sfmode]++;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] + 1);
err = 0;
for (i = 0; i < sfcount; i++) {
err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]);
@@ -2242,7 +2295,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
int j;
if (!delta)
- pmc->sfcount[sfmode]--;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
for (j = 0; j < i; j++)
(void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]);
} else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
@@ -2262,7 +2315,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf)
psf->sf_crcount = 0;
igmp_ifc_event(in_dev);
} else if (sf_setstate(pmc)) {
@@ -2278,13 +2331,13 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc)
struct ip_sf_list *tomb, *sources;
spin_lock_bh(&pmc->lock);
- tomb = pmc->tomb;
- pmc->tomb = NULL;
- sources = pmc->sources;
- pmc->sources = NULL;
+ tomb = pmc_dereference(pmc->tomb, pmc);
+ RCU_INIT_POINTER(pmc->tomb, NULL);
+ sources = pmc_dereference(pmc->sources, pmc);
+ RCU_INIT_POINTER(pmc->sources, NULL);
pmc->sfmode = MCAST_EXCLUDE;
- pmc->sfcount[MCAST_INCLUDE] = 0;
- pmc->sfcount[MCAST_EXCLUDE] = 1;
+ WRITE_ONCE(pmc->sfcount[MCAST_INCLUDE], 0);
+ WRITE_ONCE(pmc->sfcount[MCAST_EXCLUDE], 1);
spin_unlock_bh(&pmc->lock);
ip_sf_list_clear_all(tomb);
@@ -2866,20 +2919,19 @@ int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u
rv = 1;
} else if (im) {
if (src_addr) {
- spin_lock_bh(&im->lock);
- for (psf = im->sources; psf; psf = psf->sf_next) {
+ for_each_psf_rcu(im, psf) {
if (psf->sf_inaddr == src_addr)
break;
}
if (psf)
- rv = psf->sf_count[MCAST_INCLUDE] ||
- psf->sf_count[MCAST_EXCLUDE] !=
- im->sfcount[MCAST_EXCLUDE];
+ rv = READ_ONCE(psf->sf_count[MCAST_INCLUDE]) ||
+ READ_ONCE(psf->sf_count[MCAST_EXCLUDE]) !=
+ READ_ONCE(im->sfcount[MCAST_EXCLUDE]);
else
- rv = im->sfcount[MCAST_EXCLUDE] != 0;
- spin_unlock_bh(&im->lock);
- } else
+ rv = READ_ONCE(im->sfcount[MCAST_EXCLUDE]) != 0;
+ } else {
rv = 1; /* unspecified source; tentatively allow */
+ }
}
return rv;
}
--
2.55.0.860.g4b6b3295ed-goog
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH net] igmp: convert struct ip_sf_list to RCU
2026-08-26 11:59 [PATCH net] igmp: convert struct ip_sf_list to RCU Eric Dumazet
@ 2026-08-27 8:13 ` kernel test robot
2026-08-27 8:54 ` Eric Dumazet
0 siblings, 1 reply; 3+ messages in thread
From: kernel test robot @ 2026-08-27 8:13 UTC (permalink / raw)
To: Eric Dumazet, David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: oe-kbuild-all, Simon Horman, netdev, eric.dumazet, Eric Dumazet,
syzbot+a5a07d2f6a71972dc5ed
Hi Eric,
kernel test robot noticed the following build warnings:
[auto build test WARNING on net/main]
url: https://github.com/intel-lab-lkp/linux/commits/Eric-Dumazet/igmp-convert-struct-ip_sf_list-to-RCU/20260826-115913
base: net/main
patch link: https://lore.kernel.org/r/20260826115913.3381537-1-edumazet%40google.com
patch subject: [PATCH net] igmp: convert struct ip_sf_list to RCU
config: i386-randconfig-062-20260827 (https://download.01.org/0day-ci/archive/20260827/202608271655.mXCyurGJ-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
sparse: v0.6.5-rc1
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260827/202608271655.mXCyurGJ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608271655.mXCyurGJ-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
net/ipv4/igmp.c: note: in included file (through include/linux/igmp.h):
include/uapi/linux/igmp.h:60:32: sparse: sparse: array of flexible structures
>> net/ipv4/igmp.c:3114:13: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct ip_sf_list *psf @@ got struct ip_sf_list [noderef] __rcu *sf_next @@
net/ipv4/igmp.c:3114:13: sparse: expected struct ip_sf_list *psf
net/ipv4/igmp.c:3114:13: sparse: got struct ip_sf_list [noderef] __rcu *sf_next
>> net/ipv4/igmp.c:3130:21: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct ip_sf_list *psf @@ got struct ip_sf_list [noderef] __rcu *sources @@
net/ipv4/igmp.c:3130:21: sparse: expected struct ip_sf_list *psf
net/ipv4/igmp.c:3130:21: sparse: got struct ip_sf_list [noderef] __rcu *sources
net/ipv4/igmp.c:3098:29: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct ip_sf_list *psf @@ got struct ip_sf_list [noderef] __rcu *sources @@
net/ipv4/igmp.c:3098:29: sparse: expected struct ip_sf_list *psf
net/ipv4/igmp.c:3098:29: sparse: got struct ip_sf_list [noderef] __rcu *sources
net/ipv4/igmp.c:3098:29: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct ip_sf_list *psf @@ got struct ip_sf_list [noderef] __rcu *sources @@
net/ipv4/igmp.c:3098:29: sparse: expected struct ip_sf_list *psf
net/ipv4/igmp.c:3098:29: sparse: got struct ip_sf_list [noderef] __rcu *sources
vim +3114 net/ipv4/igmp.c
^1da177e4c3f41 Linus Torvalds 2005-04-16 3109
^1da177e4c3f41 Linus Torvalds 2005-04-16 3110 static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf)
^1da177e4c3f41 Linus Torvalds 2005-04-16 3111 {
^1da177e4c3f41 Linus Torvalds 2005-04-16 3112 struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
^1da177e4c3f41 Linus Torvalds 2005-04-16 3113
^1da177e4c3f41 Linus Torvalds 2005-04-16 @3114 psf = psf->sf_next;
^1da177e4c3f41 Linus Torvalds 2005-04-16 3115 while (!psf) {
^1da177e4c3f41 Linus Torvalds 2005-04-16 3116 spin_unlock_bh(&state->im->lock);
^1da177e4c3f41 Linus Torvalds 2005-04-16 3117 state->im = state->im->next;
^1da177e4c3f41 Linus Torvalds 2005-04-16 3118 while (!state->im) {
6baff15037693c Eric Dumazet 2009-11-11 3119 state->dev = next_net_device_rcu(state->dev);
^1da177e4c3f41 Linus Torvalds 2005-04-16 3120 if (!state->dev) {
^1da177e4c3f41 Linus Torvalds 2005-04-16 3121 state->idev = NULL;
^1da177e4c3f41 Linus Torvalds 2005-04-16 3122 goto out;
^1da177e4c3f41 Linus Torvalds 2005-04-16 3123 }
6baff15037693c Eric Dumazet 2009-11-11 3124 state->idev = __in_dev_get_rcu(state->dev);
^1da177e4c3f41 Linus Torvalds 2005-04-16 3125 if (!state->idev)
^1da177e4c3f41 Linus Torvalds 2005-04-16 3126 continue;
1d7138de878d1d Eric Dumazet 2010-11-12 3127 state->im = rcu_dereference(state->idev->mc_list);
^1da177e4c3f41 Linus Torvalds 2005-04-16 3128 }
^1da177e4c3f41 Linus Torvalds 2005-04-16 3129 spin_lock_bh(&state->im->lock);
^1da177e4c3f41 Linus Torvalds 2005-04-16 @3130 psf = state->im->sources;
^1da177e4c3f41 Linus Torvalds 2005-04-16 3131 }
^1da177e4c3f41 Linus Torvalds 2005-04-16 3132 out:
^1da177e4c3f41 Linus Torvalds 2005-04-16 3133 return psf;
^1da177e4c3f41 Linus Torvalds 2005-04-16 3134 }
^1da177e4c3f41 Linus Torvalds 2005-04-16 3135
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH net] igmp: convert struct ip_sf_list to RCU
2026-08-27 8:13 ` kernel test robot
@ 2026-08-27 8:54 ` Eric Dumazet
0 siblings, 0 replies; 3+ messages in thread
From: Eric Dumazet @ 2026-08-27 8:54 UTC (permalink / raw)
To: kernel test robot
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, oe-kbuild-all,
Simon Horman, netdev, eric.dumazet, syzbot+a5a07d2f6a71972dc5ed
On Thu, Aug 27, 2026 at 10:13 AM kernel test robot <lkp@intel.com> wrote:
>
> Hi Eric,
>
> kernel test robot noticed the following build warnings:
>
> [auto build test WARNING on net/main]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Eric-Dumazet/igmp-convert-struct-ip_sf_list-to-RCU/20260826-115913
> base: net/main
> patch link: https://lore.kernel.org/r/20260826115913.3381537-1-edumazet%40google.com
> patch subject: [PATCH net] igmp: convert struct ip_sf_list to RCU
> config: i386-randconfig-062-20260827 (https://download.01.org/0day-ci/archive/20260827/202608271655.mXCyurGJ-lkp@intel.com/config)
> compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
> sparse: v0.6.5-rc1
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260827/202608271655.mXCyurGJ-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202608271655.mXCyurGJ-lkp@intel.com/
>
> sparse warnings: (new ones prefixed by >>)
> net/ipv4/igmp.c: note: in included file (through include/linux/igmp.h):
> include/uapi/linux/igmp.h:60:32: sparse: sparse: array of flexible structures
> >> net/ipv4/igmp.c:3114:13: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct ip_sf_list *psf @@ got struct ip_sf_list [noderef] __rcu *sf_next @@
> net/ipv4/igmp.c:3114:13: sparse: expected struct ip_sf_list *psf
> net/ipv4/igmp.c:3114:13: sparse: got struct ip_sf_list [noderef] __rcu *sf_next
> >> net/ipv4/igmp.c:3130:21: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct ip_sf_list *psf @@ got struct ip_sf_list [noderef] __rcu *sources @@
> net/ipv4/igmp.c:3130:21: sparse: expected struct ip_sf_list *psf
> net/ipv4/igmp.c:3130:21: sparse: got struct ip_sf_list [noderef] __rcu *sources
> net/ipv4/igmp.c:3098:29: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct ip_sf_list *psf @@ got struct ip_sf_list [noderef] __rcu *sources @@
> net/ipv4/igmp.c:3098:29: sparse: expected struct ip_sf_list *psf
> net/ipv4/igmp.c:3098:29: sparse: got struct ip_sf_list [noderef] __rcu *sources
> net/ipv4/igmp.c:3098:29: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct ip_sf_list *psf @@ got struct ip_sf_list [noderef] __rcu *sources @@
> net/ipv4/igmp.c:3098:29: sparse: expected struct ip_sf_list *psf
> net/ipv4/igmp.c:3098:29: sparse: got struct ip_sf_list [noderef] __rcu *sources
>
Thanks, I will squash in V2:
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 5516329c9caa5f6f3ec7a2f633bda5d1a16eb4ec..d56355aca79776575d93e338109b768f9acf2eaa
100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -3095,7 +3095,7 @@ static inline struct ip_sf_list
*igmp_mcf_get_first(struct seq_file *seq)
im = rcu_dereference(idev->mc_list);
if (likely(im)) {
spin_lock_bh(&im->lock);
- psf = im->sources;
+ psf = pmc_dereference(im->sources, im);
if (likely(psf)) {
state->im = im;
state->idev = idev;
@@ -3111,7 +3111,7 @@ static struct ip_sf_list
*igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
{
struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
- psf = psf->sf_next;
+ psf = pmc_dereference(psf->sf_next, state->im);
while (!psf) {
spin_unlock_bh(&state->im->lock);
state->im = state->im->next;
@@ -3127,7 +3127,7 @@ static struct ip_sf_list
*igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
state->im = rcu_dereference(state->idev->mc_list);
}
spin_lock_bh(&state->im->lock);
- psf = state->im->sources;
+ psf = pmc_dereference(state->im->sources, state->im);
}
out:
return psf;
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-27 8:55 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 11:59 [PATCH net] igmp: convert struct ip_sf_list to RCU Eric Dumazet
2026-08-27 8:13 ` kernel test robot
2026-08-27 8:54 ` Eric Dumazet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox