[PATCH 20/23] [PATCH] bridge: fix RCU race on device removal

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



-stable review patch.  If anyone has any objections, please let us know.
------------------

Patch to 2.6.15 stable kernel to fix race conditions on device
removal.  These are reproducible by doing delif while packets are
in flight.

Signed-off-by: Stephen Hemminger <[email protected]>
Signed-off-by: Chris Wright <[email protected]>
---

 net/bridge/br_if.c       |    7 ++-----
 net/bridge/br_input.c    |   10 ++++++++--
 net/bridge/br_stp_bpdu.c |    8 ++++++--
 3 files changed, 16 insertions(+), 9 deletions(-)

Index: linux-2.6.15.3/net/bridge/br_if.c
===================================================================
--- linux-2.6.15.3.orig/net/bridge/br_if.c
+++ linux-2.6.15.3/net/bridge/br_if.c
@@ -99,7 +99,6 @@ static void del_nbp(struct net_bridge_po
 	struct net_bridge *br = p->br;
 	struct net_device *dev = p->dev;
 
-	dev->br_port = NULL;
 	dev_set_promiscuity(dev, -1);
 
 	spin_lock_bh(&br->lock);
@@ -110,9 +109,7 @@ static void del_nbp(struct net_bridge_po
 
 	list_del_rcu(&p->list);
 
-	del_timer_sync(&p->message_age_timer);
-	del_timer_sync(&p->forward_delay_timer);
-	del_timer_sync(&p->hold_timer);
+	rcu_assign_pointer(dev->br_port, NULL);
 	
 	call_rcu(&p->rcu, destroy_nbp_rcu);
 }
@@ -217,7 +214,6 @@ static struct net_bridge_port *new_nbp(s
 	p->dev = dev;
 	p->path_cost = cost;
  	p->priority = 0x8000 >> BR_PORT_BITS;
-	dev->br_port = p;
 	p->port_no = index;
 	br_init_port(p);
 	p->state = BR_STATE_DISABLED;
@@ -360,6 +356,7 @@ int br_add_if(struct net_bridge *br, str
 	else if ((err = br_sysfs_addif(p)))
 		del_nbp(p);
 	else {
+		rcu_assign_pointer(dev->br_port, p);
 		dev_set_promiscuity(dev, 1);
 
 		list_add_rcu(&p->list, &br->port_list);
Index: linux-2.6.15.3/net/bridge/br_input.c
===================================================================
--- linux-2.6.15.3.orig/net/bridge/br_input.c
+++ linux-2.6.15.3/net/bridge/br_input.c
@@ -45,11 +45,17 @@ static void br_pass_frame_up(struct net_
 int br_handle_frame_finish(struct sk_buff *skb)
 {
 	const unsigned char *dest = eth_hdr(skb)->h_dest;
-	struct net_bridge_port *p = skb->dev->br_port;
-	struct net_bridge *br = p->br;
+	struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
+	struct net_bridge *br;
 	struct net_bridge_fdb_entry *dst;
 	int passedup = 0;
 
+	if (unlikely(!p || p->state == BR_STATE_DISABLED)) {
+		kfree_skb(skb);
+		return 0;
+	}
+
+	br = p->br;
 	/* insert into forwarding database after filtering to avoid spoofing */
 	br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
 
Index: linux-2.6.15.3/net/bridge/br_stp_bpdu.c
===================================================================
--- linux-2.6.15.3.orig/net/bridge/br_stp_bpdu.c
+++ linux-2.6.15.3/net/bridge/br_stp_bpdu.c
@@ -136,10 +136,13 @@ static const unsigned char header[6] = {
 /* NO locks */
 int br_stp_handle_bpdu(struct sk_buff *skb)
 {
-	struct net_bridge_port *p = skb->dev->br_port;
-	struct net_bridge *br = p->br;
+	struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
+	struct net_bridge *br;
 	unsigned char *buf;
 
+	if (!p)
+		goto err;
+
 	/* insert into forwarding database after filtering to avoid spoofing */
 	br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
 
@@ -150,6 +153,7 @@ int br_stp_handle_bpdu(struct sk_buff *s
 
 	buf = skb_pull(skb, sizeof(header));
 
+	br = p->br;
 	spin_lock_bh(&br->lock);
 	if (p->state == BR_STATE_DISABLED 
 	    || !(br->dev->flags & IFF_UP)

--
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux