Skip to contentSkip to conversation

igmp: fix ip_mc_sf_allow race

PATCH In review Flavio Leitner 7 participants 16 messages Dec 30, 2009 – Feb 2, 2010

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 igmp: fix ip_mc_sf_allow race

Almost all igmp functions accessing inet->mc_list are protected by rtnl_lock(), but there is one exception which is ip_mc_sf_allow(), so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group remove an entry while ip_mc_sf_allow is running causing a crash.

---
 net/ipv4/igmp.c |   56 +++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 44 insertions(+), 12 deletions(-)
net/ipv4/igmp.c +44 -12
index 76c0840..d1e2a0c 100644
@@ -1799,7 +1799,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
 	iml->next = inet->mc_list;
 	iml->sflist = NULL;
 	iml->sfmode = MCAST_EXCLUDE;
-	inet->mc_list = iml;
+	rcu_assign_pointer(inet->mc_list, iml);
 	ip_mc_inc_group(in_dev, addr);
 	err = 0;
 done:
@@ -1854,11 +1854,12 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
 
 		(void) ip_mc_leave_src(sk, iml, in_dev);
 
-		*imlp = iml->next;
+		rcu_assign_pointer(*imlp, iml->next);
 
 		if (in_dev)
 			ip_mc_dec_group(in_dev, group);
 		rtnl_unlock();
+		synchronize_rcu();
 		sock_kfree_s(sk, iml, sizeof(*iml));
 		return 0;
 	}
@@ -2209,30 +2210,40 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif)
 	struct ip_mc_socklist *pmc;
 	struct ip_sf_socklist *psl;
 	int i;
+	int ret;
 
+	ret = 1;
 	if (!ipv4_is_multicast(loc_addr))
-		return 1;
+		goto out;
 
-	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
+	rcu_read_lock();
+	for (pmc=rcu_dereference(inet->mc_list); pmc; pmc=rcu_dereference(pmc->next)) {
 		if (pmc->multi.imr_multiaddr.s_addr == loc_addr &&
 		    pmc->multi.imr_ifindex == dif)
 			break;
 	}
+	ret = inet->mc_all;
 	if (!pmc)
-		return inet->mc_all;
+		goto unlock;
 	psl = pmc->sflist;
+	ret = (pmc->sfmode == MCAST_EXCLUDE);
 	if (!psl)
-		return pmc->sfmode == MCAST_EXCLUDE;
+		goto unlock;
 
 	for (i=0; i<psl->sl_count; i++) {
 		if (psl->sl_addr[i] == rmt_addr)
 			break;
 	}
+	ret = 0;
 	if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count)
-		return 0;
+		goto unlock;
 	if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
-		return 0;
-	return 1;
+		goto unlock;
+	ret = 1;
+unlock:
+	rcu_read_unlock();
+out:
+	return ret;
 }
 
 /*
@@ -2243,15 +2254,21 @@ void ip_mc_drop_socket(struct sock *sk)
 {
 	struct inet_sock *inet = inet_sk(sk);
 	struct ip_mc_socklist *iml;
+	struct ip_mc_socklist *rmlist;
 	struct net *net = sock_net(sk);
 
-	if (inet->mc_list == NULL)
+	rcu_read_lock();
+	if (rcu_dereference(inet->mc_list) == NULL) {
+		rcu_read_unlock();
 		return;
+	}
+	rcu_read_unlock();
 
 	rtnl_lock();
+	rmlist = NULL;
 	while ((iml = inet->mc_list) != NULL) {
 		struct in_device *in_dev;
-		inet->mc_list = iml->next;
+		rcu_assign_pointer(inet->mc_list, iml->next);
 
 		in_dev = inetdev_by_index(net, iml->multi.imr_ifindex);
 		(void) ip_mc_leave_src(sk, iml, in_dev);
@@ -2259,9 +2276,24 @@ void ip_mc_drop_socket(struct sock *sk)
 			ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
 			in_dev_put(in_dev);
 		}
-		sock_kfree_s(sk, iml, sizeof(*iml));
+		if (rmlist == NULL)  {
+			iml->next = NULL;
+			rmlist = iml;
+		}
+		else {
+			iml->next = rmlist->next;
+			rmlist->next = iml;
+		}
 	}
 	rtnl_unlock();
+
+	synchronize_rcu();
+
+	while (rmlist != NULL) {
+		iml = rmlist;
+		rmlist = iml->next;
+		sock_kfree_s(sk, iml, sizeof(*iml));
+	}
 }
 
 int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto)
Signature · 1 line
1.6.5.2
maintainer
#1 lore raw

From: Flavio Leitner <[email protected]>
Date: Wed, 30 Dec 2009 12:23:25 -0200

Quoted 6 lines

Almost all igmp functions accessing inet->mc_list are protected by rtnl_lock(), but there is one exception which is ip_mc_sf_allow(), so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group remove an entry while ip_mc_sf_allow is running causing a crash.

Have you triggered this in practice or is this due purely to code inspection?

That new synchronize_rcu() is very expensive and will decrease the rate at which groups can be joined and left, _especially_ on high cpu count machines.

I do not think it is therefore a suitable problem to this race, if it does in fact exist.

#2 lore raw
Quoted 8 lines · David Miller

From: Flavio Leitner <[email protected]>
Date: Wed, 30 Dec 2009 12:23:25 -0200

Quoted 5 lines

Almost all igmp functions accessing inet->mc_list are protected by rtnl_lock(), but there is one exception which is ip_mc_sf_allow(), so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group remove an entry while ip_mc_sf_allow is running causing a crash.

Quoted 4 lines
Quoted 1 line

Have you triggered this in practice or is this due purely to code inspection?

I had to modify the code to reproduce introducing a delay in ip_mc_sf_allow(), but a customer is able to reproduce it when avahi-daemon runs at boot time.

CPU: Intel(R) Xeon(R) CPU X5570 @ 2.93GHz stepping 05

BUG: unable to handle kernel paging request at virtual address 005e0005
printing eip:
c05f2194
*pde = 00000000
Oops: 0000 [#1]
SMP
last sysfs file: /devices/pci0000:7f/0000:7f:06.0/irq
Modules linked in: nfs lockd fscache nfs_acl autofs4 hidp l2cap

bluetooth sunrpc 8021q ipv6 xfrm_nalgo crypto_api dm_multipath scsi_dh video hwmon backlight sbs i2c_ec i2c_core but ton battery asus_acpi ac parport_pc lp parport sg e1000(U) pcspkr dm_raid45 dm_message dm_ region_hash dm_mem_cache dm_snapshot dm_zero dm_mirror dm_log dm_mod hfcldd(FU) sd_mod scs i_mod hfcldd_conf(U) hraslog_link(U) ext3 jbd uhci_hcd ohci_hcd ehci_hcd

CPU:    0
EIP:    0060:[<c05f2194>]    Tainted: GF     VLI
EFLAGS: 00210202   (2.6.18-128.el5PAE #1)

EIP is at ip_mc_sf_allow+0x20/0x79
eax: 005e0001 ebx: f6cb9100 ecx: 00000008 edx: fb0000e0
esi: 5acb10ac edi: f63414e9 ebp: f7ae3200 esp: c0732ea4
ds: 007b es: 007b ss: 0068
Process xlinpack_xeon32 (pid: 5194, ti=c0732000 task=cfd2daa0 task.ti=f5d30000)

Stack: f6cb9108 f6cb9100 c05ea1eb 00000008 d03e9034 5acb10ac fb0000e0 e9140000
      00000008 e91414e9 f7ae3200 c06ab4a8 00000000 00000000 c05ce1d5 f7ae3200
      00000000 f7ae3200 d03e9020 c05ce042 f7ae3200 c07d6988 c06ab560 00000008
Call Trace:
[<c05ea1eb>] udp_rcv+0x1f4/0x514
[<c05ce1d5>] ip_local_deliver+0x159/0x204
[<c05ce042>] ip_rcv+0x46f/0x4a9
[<c05b397d>] netif_receive_skb+0x30c/0x330
[<f8924be0>] e1000_clean_rx_irq+0xf0/0x3e0 [e1000]
[<f8924af0>] e1000_clean_rx_irq+0x0/0x3e0 [e1000]
[<f8922bd4>] e1000_clean+0xf4/0x340 [e1000]
[<c04074d6>] do_IRQ+0xb5/0xc3
[<c05b52d4>] net_rx_action+0x92/0x175
[<c042900f>] __do_softirq+0x87/0x114
[<c04073d7>] do_softirq+0x52/0x9c
[<c04059d7>] apic_timer_interrupt+0x1f/0x24
=======================
Code: 81 c4 8c 00 00 00 5b 5e 5f 5d c3 56 89 ce 53 89 c3 8b 4c 24 0c 89

d0 25 f0 00 00 00
3d e0 00 00 00 75 59 8b 83 84 01 00 00 eb 0c <39> 50 04 75 05 39 48 0c 74 08 8b 00 85 c0 75 f0 eb 3f 8b 50 14
EIP: [<c05f2194>] ip_mc_sf_allow+0x20/0x79 SS:ESP 0068:c0732ea4 <0>Kernel panic - not syncing: Fatal exception in interrupt --- snip ---

Quoted 3 lines

That new synchronize_rcu() is very expensive and will decrease the rate at which groups can be joined and left, _especially_ on high cpu count machines.

Well, I tried using read-write locking but then the packet reception was slower while another task was playing with multicasting groups.

Then, I tried using call_rcu() to avoid the problem you are saying, but when you stop the reproducer, sk_free() will warn printing "optmem leakage.." because the rcu callback didn't run yet.

Quoted 6 lines

I do not think it is therefore a suitable problem to this race, if it does in fact exist.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 1 line
Flavio
#3 lore raw
Quoted 5 lines · Flavio Leitner

Then, I tried using call_rcu() to avoid the problem you are saying, but when you stop the reproducer, sk_free() will warn printing "optmem leakage.." because the rcu callback didn't run yet.

This is probably because your call_rcu() callback was trying to call sock_kfree_s() ?

 		rtnl_unlock();
		call_rcu(&iml->lock, callback_func)

callback_func()

{
	sock_kfree_s(sk, iml, sizeof(*iml));
}

Take a look at sock_kfree_s() definition :

void sock_kfree_s(struct sock *sk, void *mem, int size)

{
        kfree(mem);
        atomic_sub(size, &sk->sk_omem_alloc);
}

You can certainly try :

	rtnl_unlock();
	atomic_sub(sizeof(*iml), sk->sk_omem_alloc);
	call_rcu(&iml->rcu, kfree);

(immediate sk_omem_alloc handling, but deferred kfree())

#4 lore raw
Quoted 9 lines · Eric Dumazet
Quoted 5 lines · Flavio Leitner

Then, I tried using call_rcu() to avoid the problem you are saying, but when you stop the reproducer, sk_free() will warn printing "optmem leakage.." because the rcu callback didn't run yet.

This is probably because your call_rcu() callback was trying to call sock_kfree_s() ?

yes, correct.

Quoted 20 lines
 		rtnl_unlock();
		call_rcu(&iml->lock, callback_func)

callback_func()

{
	sock_kfree_s(sk, iml, sizeof(*iml));
}

Take a look at sock_kfree_s() definition :

void sock_kfree_s(struct sock *sk, void *mem, int size)

{
        kfree(mem);
        atomic_sub(size, &sk->sk_omem_alloc);
}
Quoted 7 lines

You can certainly try :

	rtnl_unlock();
	atomic_sub(sizeof(*iml), sk->sk_omem_alloc);
	call_rcu(&iml->rcu, kfree);

(immediate sk_omem_alloc handling, but deferred kfree())

Ok, below is the new version using call_rcu(). I'm still running my tests here, so I'm planning to resubmit it later if this version is okay with you.

thanks!

include/linux/igmp.h +1 -0
index 724c27e..9cccd16 100644
@@ -170,6 +170,7 @@ struct ip_mc_socklist {
 	struct ip_mreqn		multi;
 	unsigned int		sfmode;		/* MCAST_{INCLUDE,EXCLUDE} */
 	struct ip_sf_socklist	*sflist;
+	struct rcu_head		rcu;
 };
 
 struct ip_sf_list {
net/ipv4/igmp.c +44 -14

119 lines not on this page · raw message

Signature · 1 line
1.6.2.3
#5 lore raw
Quoted 39 lines · Flavio Leitner
Quoted 9 lines · Eric Dumazet
Quoted 5 lines · Flavio Leitner

Then, I tried using call_rcu() to avoid the problem you are saying, but when you stop the reproducer, sk_free() will warn printing "optmem leakage.." because the rcu callback didn't run yet.

This is probably because your call_rcu() callback was trying to call sock_kfree_s() ?

yes, correct.

Quoted 26 lines
 		rtnl_unlock();
		call_rcu(&iml->lock, callback_func)

callback_func()

{
	sock_kfree_s(sk, iml, sizeof(*iml));
}

Take a look at sock_kfree_s() definition :

void sock_kfree_s(struct sock *sk, void *mem, int size)

{
        kfree(mem);
        atomic_sub(size, &sk->sk_omem_alloc);
}

You can certainly try :

	rtnl_unlock();
	atomic_sub(sizeof(*iml), sk->sk_omem_alloc);
	call_rcu(&iml->rcu, kfree);
Quoted 5 lines
Quoted 1 line

(immediate sk_omem_alloc handling, but deferred kfree())

Ok, below is the new version using call_rcu(). I'm still running my tests here, so I'm planning to resubmit it later if this version is okay with you.

It seems fine, but please make ip_mc_socklist_reclaim() static :

net/ipv4/igmp.c static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, · line 1838

Quoted 11 lines
net/ipv4/igmp.c +11 -0
+
+void ip_mc_socklist_reclaim(struct rcu_head *rp)
+{
+	struct ip_mc_socklist *iml;
+
+	iml = container_of(rp, struct ip_mc_socklist, rcu);
+	/* sk_omem_alloc should have been decreased by the caller*/
+	kfree(iml);
+}
+
+

Thanks

#7 lore raw
Quoted 8 lines · David Stevens

For readability, instead of:

        ret = X;
        if (condition)
                goto out;

I prefer:

Quoted 8 lines
        if (condition) {
                ret = X;
                goto out;
        }

which makes it clear that ret is for the return and not some random state change unrelated to the condition.

Linus argument for this common Linux coding style is that generated code is smaller

	mov	$-6,%eax
	cmp	condition
	je	out

versus

	cmp	condition
	jne	.ok
	mov	$-6,%eax
	jmp	out

.ok:

#8 lore raw

PATCH 1/1 igmp: fix ip_mc_sf_allow race [v3]

Almost all igmp functions accessing inet->mc_list are protected by rtnl_lock(), but there is one exception which is ip_mc_sf_allow(), so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group remove an entry while ip_mc_sf_allow is running causing a crash.

---
 include/linux/igmp.h |    1 +
 net/ipv4/igmp.c      |   58 +++++++++++++++++++++++++++++++++++++------------
 2 files changed, 45 insertions(+), 14 deletions(-)
include/linux/igmp.h +1 -0
index 724c27e..9cccd16 100644
@@ -170,6 +170,7 @@ struct ip_mc_socklist {
 	struct ip_mreqn		multi;
 	unsigned int		sfmode;		/* MCAST_{INCLUDE,EXCLUDE} */
 	struct ip_sf_socklist	*sflist;
+	struct rcu_head		rcu;
 };
 
 struct ip_sf_list {
net/ipv4/igmp.c +44 -14

119 lines not on this page · raw message

Signature · 1 line
1.6.2.3
Acked-by on v1 1/1
#9 lore raw

Re: [PATCH] igmp: fix ip_mc_sf_allow race [v3]

Quoted 6 lines · Flavio Leitner

Almost all igmp functions accessing inet->mc_list are protected by rtnl_lock(), but there is one exception which is ip_mc_sf_allow(), so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group remove an entry while ip_mc_sf_allow is running causing a crash.

Small notes : in ip_mc_drop_socket()

// rcu_read_lock()/unlock() seems not really needed here, we only want to avoid //the fill rtnlçlock() in case this socket have a NULL mc_list.

rcu_read_lock();

if (rcu_dereference(inet->mc_list) == NULL) {
	rcu_read_unlock();
	return;
}

rcu_read_unlock();

rtnl_lock();
while ((iml = rcu_dereference(inet->mc_list)) != NULL) {

rcu_dereference() is not really needed here, since you own RTNL

#10 lore raw

Re: [PATCH] igmp: fix ip_mc_sf_allow race [v3]

In reply to Flavio Leitner
Quoted 4 lines · On Tue, 5 Jan 2010 18:52:22 -0200 Flavio Leitner <[email protected]> wrote:
net/ipv4/igmp.c +0 -0
@@ -2245,13 +2269,17 @@ void ip_mc_drop_socket(struct sock *sk)
 	struct ip_mc_socklist *iml;
 	struct net *net = sock_net(sk);
 

net/ipv4/igmp.c void ip_mc_drop_socket(struct sock *sk) · line 2252

Quoted 10 lines
net/ipv4/igmp.c +5 -2
-	if (inet->mc_list == NULL)
+	rcu_read_lock();
+	if (rcu_dereference(inet->mc_list) == NULL) {
+		rcu_read_unlock();
 		return;
+	}
+	rcu_read_unlock();
 
 	rtnl_lock();
-	while ((iml = i

All this would be cleaner if mc_list was using list_head and the existing list_head_rcu stuff.

#11 lore raw

Re: [PATCH] igmp: fix ip_mc_sf_allow race [v3]

In reply to Flavio Leitner
Quoted 4 lines · Flavio Leitner

Almost all igmp functions accessing inet->mc_list are protected by rtnl_lock(), but there is one exception which is ip_mc_sf_allow(), so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group remove an entry while ip_mc_sf_allow is running causing a crash.

Looks like a good start from an RCU perspective, though I don't claim to understand networking locking design. A couple of questions below.

Thanx, Paul

Quoted 50 lines
---
 include/linux/igmp.h |    1 +
 net/ipv4/igmp.c      |   58 +++++++++++++++++++++++++++++++++++++------------
 2 files changed, 45 insertions(+), 14 deletions(-)
include/linux/igmp.h +1 -0
index 724c27e..9cccd16 100644
@@ -170,6 +170,7 @@ struct ip_mc_socklist {
 	struct ip_mreqn		multi;
 	unsigned int		sfmode;		/* MCAST_{INCLUDE,EXCLUDE} */
 	struct ip_sf_socklist	*sflist;
+	struct rcu_head		rcu;
 };
 
 struct ip_sf_list {
net/ipv4/igmp.c +12 -1
index 76c0840..61ff685 100644
@@ -1799,7 +1799,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
 	iml->next = inet->mc_list;
 	iml->sflist = NULL;
 	iml->sfmode = MCAST_EXCLUDE;
-	inet->mc_list = iml;
+	rcu_assign_pointer(inet->mc_list, iml);
 	ip_mc_inc_group(in_dev, addr);
 	err = 0;
 done:
@@ -1825,6 +1825,17 @@ static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
 	return err;
 }
 
+
+static void ip_mc_socklist_reclaim(struct rcu_head *rp)
+{
+	struct ip_mc_socklist *iml;
+
+	iml = container_of(rp, struct ip_mc_socklist, rcu);
+	/* sk_omem_alloc should have been decreased by the caller*/
+	kfree(iml);
+}
+
+
 /*
  *	Ask a socket to leave a group.
  */

net/ipv4/igmp.c int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) · line 1866

Quoted 2 lines
net/ipv4/igmp.c +0 -0
@@ -1854,12 +1865,15 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
 
 		(void) ip_mc_leave_src(sk, iml, in_dev);

Suppose some other CPU invokes ip_mc_sf_allow() at this point. Will that CPU be OK with the current state of the structure pointed to by iml? If not, some of the above code might need to be deferred to follow the grace period.

Quoted 63 lines
net/ipv4/igmp.c +22 -9
-		*imlp = iml->next;
+		rcu_assign_pointer(*imlp, iml->next);
 
 		if (in_dev)
 			ip_mc_dec_group(in_dev, group);
+
 		rtnl_unlock();
-		sock_kfree_s(sk, iml, sizeof(*iml));
+		/* decrease mem now to avoid the memleak warning */
+		atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
+		call_rcu(&iml->rcu, ip_mc_socklist_reclaim);
 		return 0;
 	}
 	if (!in_dev)
@@ -2209,30 +2223,40 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif)
 	struct ip_mc_socklist *pmc;
 	struct ip_sf_socklist *psl;
 	int i;
+	int ret;
 
+	ret = 1;
 	if (!ipv4_is_multicast(loc_addr))
-		return 1;
+		goto out;
 
-	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
+	rcu_read_lock();
+	for (pmc=rcu_dereference(inet->mc_list); pmc; pmc=rcu_dereference(pmc->next)) {
 		if (pmc->multi.imr_multiaddr.s_addr == loc_addr &&
 		    pmc->multi.imr_ifindex == dif)
 			break;
 	}
+	ret = inet->mc_all;
 	if (!pmc)
-		return inet->mc_all;
+		goto unlock;
 	psl = pmc->sflist;
+	ret = (pmc->sfmode == MCAST_EXCLUDE);
 	if (!psl)
-		return pmc->sfmode == MCAST_EXCLUDE;
+		goto unlock;
 
 	for (i=0; i<psl->sl_count; i++) {
 		if (psl->sl_addr[i] == rmt_addr)
 			break;
 	}
+	ret = 0;
 	if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count)
-		return 0;
+		goto unlock;
 	if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
-		return 0;
-	return 1;
+		goto unlock;
+	ret = 1;
+unlock:
+	rcu_read_unlock();
+out:
+	return ret;
 }
 
 /*

net/ipv4/igmp.c void ip_mc_drop_socket(struct sock *sk) · line 2277

Quoted 10 lines
net/ipv4/igmp.c +5 -1
@@ -2245,13 +2269,17 @@ void ip_mc_drop_socket(struct sock *sk)
 	struct ip_mc_socklist *iml;
 	struct net *net = sock_net(sk);
 
-	if (inet->mc_list == NULL)
+	rcu_read_lock();
+	if (rcu_dereference(inet->mc_list) == NULL) {
+		rcu_read_unlock();
 		return;
+	}
+	rcu_read_unlock();

I don't understand what rcu_read_lock() is protecting here. The test is still unstable -- just after finding inet->mc_list non-NULL, ip_mc_leave_group() might cause it to become NULL.

Is there a need to protect sock_net(sk)? (I don't believe so, but then again, I don't claim to understand locking in Linux networking.) If there is no need, it should be possible to drop the rcu_read_lock(), rcu_read_unlock(), and rcu_dereference() above. (You might want them for documentation purposes, as they aren't hurting anything, just wondering what the intent is.)

Quoted 27 lines
net/ipv4/igmp.c +5 -3
 	rtnl_lock();
-	while ((iml = inet->mc_list) != NULL) {
+	while ((iml = rcu_dereference(inet->mc_list)) != NULL) {
 		struct in_device *in_dev;
-		inet->mc_list = iml->next;
+		rcu_assign_pointer(inet->mc_list, iml->next);
 
 		in_dev = inetdev_by_index(net, iml->multi.imr_ifindex);
 		(void) ip_mc_leave_src(sk, iml, in_dev);
@@ -2259,7 +2287,9 @@ void ip_mc_drop_socket(struct sock *sk)
 			ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
 			in_dev_put(in_dev);
 		}
-		sock_kfree_s(sk, iml, sizeof(*iml));
+		/* decrease mem now to avoid the memleak warning */
+		atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
+		call_rcu(&iml->rcu, ip_mc_socklist_reclaim);
 	}
 	rtnl_unlock();
 }
Signature · 6 lines
1.6.2.3

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#12 lore raw

Re: [PATCH] igmp: fix ip_mc_sf_allow race [v3]

Quoted 12 lines · On Wed, 6 Jan 2010 08:40:27 -0800 "Paul E. McKenney"…

net/ipv4/igmp.c void ip_mc_drop_socket(struct sock *sk) · line 2277

Quoted 7 lines
net/ipv4/igmp.c +5 -1
-	if (inet->mc_list == NULL)
+	rcu_read_lock();
+	if (rcu_dereference(inet->mc_list) == NULL) {
+		rcu_read_unlock();
 		return;
+	}
+	rcu_read_unlock();  

I don't understand what rcu_read_lock() is protecting here. The test is still unstable -- just after finding inet->mc_list non-NULL, ip_mc_leave_group() might cause it to become NULL.

Quoted 6 lines

Is there a need to protect sock_net(sk)? (I don't believe so, but then again, I don't claim to understand locking in Linux networking.) If there is no need, it should be possible to drop the rcu_read_lock(), rcu_read_unlock(), and rcu_dereference() above. (You might want them for documentation purposes, as they aren't hurting anything, just wondering what the intent is.)

I think code is trying to avoid looking at mc_list if no multicast addresses. But it is an unsafe check.

If mc_list was just converted to list_head this would all be clearer

#13 lore raw

Re: [PATCH] igmp: fix ip_mc_sf_allow race [v3]

Quoted 22 lines · Stephen Hemminger
Quoted 12 lines · On Wed, 6 Jan 2010 08:40:27 -0800 "Paul E. McKenney"…

net/ipv4/igmp.c void ip_mc_drop_socket(struct sock *sk) · line 2277

Quoted 7 lines
net/ipv4/igmp.c +5 -1
-	if (inet->mc_list == NULL)
+	rcu_read_lock();
+	if (rcu_dereference(inet->mc_list) == NULL) {
+		rcu_read_unlock();
 		return;
+	}
+	rcu_read_unlock();  

I don't understand what rcu_read_lock() is protecting here. The test is still unstable -- just after finding inet->mc_list non-NULL, ip_mc_leave_group() might cause it to become NULL.

Quoted 6 lines

Is there a need to protect sock_net(sk)? (I don't believe so, but then again, I don't claim to understand locking in Linux networking.) If there is no need, it should be possible to drop the rcu_read_lock(), rcu_read_unlock(), and rcu_dereference() above. (You might want them for documentation purposes, as they aren't hurting anything, just wondering what the intent is.)

Quoted 2 lines

I think code is trying to avoid looking at mc_list if no multicast addresses. But it is an unsafe check.

Fair enough! Might be worth a comment saying that the rcu_read_lock(), rcu_read_unlock()s, and rcu_dereference() are just for show.

Quoted 1 line

If mc_list was just converted to list_head this would all be clearer

Agreed! ;-)

Thanx, Paul

#14 lore raw

PATCH 1/1 igmp: fix ip_mc_sf_allow race [v5]

In reply to Flavio Leitner

Hello,

This is another version considering the suggestions. Thanks! I didn't convert to list_head yet because a lot more code needs to be changed so I'd rather leave that to another patch. 8<---------

Almost all igmp functions accessing inet->mc_list are protected by rtnl_lock(), but there is one exception which is ip_mc_sf_allow(), so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group remove an entry while ip_mc_sf_allow is running causing a crash.

---
 include/linux/igmp.h |    2 +
 net/ipv4/igmp.c      |   83 +++++++++++++++++++++++++++++++++++++------------
 2 files changed, 64 insertions(+), 21 deletions(-)
include/linux/igmp.h +2 -0
index 724c27e..93fc244 100644
@@ -153,6 +153,7 @@ extern int sysctl_igmp_max_msf;
 struct ip_sf_socklist {
 	unsigned int		sl_max;
 	unsigned int		sl_count;
+	struct rcu_head		rcu;
 	__be32			sl_addr[0];
 };
 
@@ -170,6 +171,7 @@ struct ip_mc_socklist {
 	struct ip_mreqn		multi;
 	unsigned int		sfmode;		/* MCAST_{INCLUDE,EXCLUDE} */
 	struct ip_sf_socklist	*sflist;
+	struct rcu_head		rcu;
 };
 
 struct ip_sf_list {
net/ipv4/igmp.c +62 -21

169 lines not on this page · raw message

Signature · 1 line
1.6.5.2
maintainer Applied
#15 lore raw

Re: [PATCH] igmp: fix ip_mc_sf_allow race [v5]

From: Flavio Leitner <[email protected]>
Date: Thu, 28 Jan 2010 14:13:38 -0200

Quoted 7 lines

Hello,

This is another version considering the suggestions. Thanks! I didn't convert to list_head yet because a lot more code needs to be changed so I'd rather leave that to another patch. 8<---------

Quoted 6 lines

Almost all igmp functions accessing inet->mc_list are protected by rtnl_lock(), but there is one exception which is ip_mc_sf_allow(), so there is a chance of either ip_mc_drop_socket or ip_mc_leave_group remove an entry while ip_mc_sf_allow is running causing a crash.

Applied to net-next-2.6, thanks.