aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
authorstephen hemminger <shemminger@vyatta.com>2010-11-15 01:38:13 -0500
committerDavid S. Miller <davem@davemloft.net>2010-11-15 14:13:17 -0500
commitb5ed54e94d324f17c97852296d61a143f01b227a (patch)
tree2104d3cde24efc1145bc71beb4b61d79ed841132 /net/bridge
parent61391cde9eefac5cfcf6d214aa80c77e58b1626b (diff)
bridge: fix RCU races with bridge port
The macro br_port_exists() is not enough protection when only RCU is being used. There is a tiny race where other CPU has cleared port handler hook, but is bridge port flag might still be set. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br_fdb.c15
-rw-r--r--net/bridge/br_if.c5
-rw-r--r--net/bridge/br_netfilter.c13
-rw-r--r--net/bridge/br_netlink.c10
-rw-r--r--net/bridge/br_notify.c2
-rw-r--r--net/bridge/br_private.h14
-rw-r--r--net/bridge/br_stp_bpdu.c8
-rw-r--r--net/bridge/netfilter/ebtables.c11
8 files changed, 44 insertions, 34 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 90512ccfd3e9..2872393b2939 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -238,15 +238,18 @@ struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
238int br_fdb_test_addr(struct net_device *dev, unsigned char *addr) 238int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
239{ 239{
240 struct net_bridge_fdb_entry *fdb; 240 struct net_bridge_fdb_entry *fdb;
241 struct net_bridge_port *port;
241 int ret; 242 int ret;
242 243
243 if (!br_port_exists(dev))
244 return 0;
245
246 rcu_read_lock(); 244 rcu_read_lock();
247 fdb = __br_fdb_get(br_port_get_rcu(dev)->br, addr); 245 port = br_port_get_rcu(dev);
248 ret = fdb && fdb->dst->dev != dev && 246 if (!port)
249 fdb->dst->state == BR_STATE_FORWARDING; 247 ret = 0;
248 else {
249 fdb = __br_fdb_get(port->br, addr);
250 ret = fdb && fdb->dst->dev != dev &&
251 fdb->dst->state == BR_STATE_FORWARDING;
252 }
250 rcu_read_unlock(); 253 rcu_read_unlock();
251 254
252 return ret; 255 return ret;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 89ad25a76202..427f90a8ab7b 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -475,11 +475,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
475{ 475{
476 struct net_bridge_port *p; 476 struct net_bridge_port *p;
477 477
478 if (!br_port_exists(dev))
479 return -EINVAL;
480
481 p = br_port_get(dev); 478 p = br_port_get(dev);
482 if (p->br != br) 479 if (!p || p->br != br)
483 return -EINVAL; 480 return -EINVAL;
484 481
485 del_nbp(p); 482 del_nbp(p);
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 865fd7634b67..ce8b2eed4e73 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -131,17 +131,18 @@ void br_netfilter_rtable_init(struct net_bridge *br)
131 131
132static inline struct rtable *bridge_parent_rtable(const struct net_device *dev) 132static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
133{ 133{
134 if (!br_port_exists(dev)) 134 struct net_bridge_port *port;
135 return NULL; 135
136 return &br_port_get_rcu(dev)->br->fake_rtable; 136 port = br_port_get_rcu(dev);
137 return port ? &port->br->fake_rtable : NULL;
137} 138}
138 139
139static inline struct net_device *bridge_parent(const struct net_device *dev) 140static inline struct net_device *bridge_parent(const struct net_device *dev)
140{ 141{
141 if (!br_port_exists(dev)) 142 struct net_bridge_port *port;
142 return NULL;
143 143
144 return br_port_get_rcu(dev)->br->dev; 144 port = br_port_get_rcu(dev);
145 return port ? port->br->dev : NULL;
145} 146}
146 147
147static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb) 148static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 4a6a378c84e3..e3de0a428f5d 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -119,11 +119,13 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
119 119
120 idx = 0; 120 idx = 0;
121 for_each_netdev(net, dev) { 121 for_each_netdev(net, dev) {
122 struct net_bridge_port *port = br_port_get(dev);
123
122 /* not a bridge port */ 124 /* not a bridge port */
123 if (!br_port_exists(dev) || idx < cb->args[0]) 125 if (!port || idx < cb->args[0])
124 goto skip; 126 goto skip;
125 127
126 if (br_fill_ifinfo(skb, br_port_get(dev), 128 if (br_fill_ifinfo(skb, port,
127 NETLINK_CB(cb->skb).pid, 129 NETLINK_CB(cb->skb).pid,
128 cb->nlh->nlmsg_seq, RTM_NEWLINK, 130 cb->nlh->nlmsg_seq, RTM_NEWLINK,
129 NLM_F_MULTI) < 0) 131 NLM_F_MULTI) < 0)
@@ -169,9 +171,9 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
169 if (!dev) 171 if (!dev)
170 return -ENODEV; 172 return -ENODEV;
171 173
172 if (!br_port_exists(dev))
173 return -EINVAL;
174 p = br_port_get(dev); 174 p = br_port_get(dev);
175 if (!p)
176 return -EINVAL;
175 177
176 /* if kernel STP is running, don't allow changes */ 178 /* if kernel STP is running, don't allow changes */
177 if (p->br->stp_enabled == BR_KERNEL_STP) 179 if (p->br->stp_enabled == BR_KERNEL_STP)
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 404d4e14c6a7..ef2175c8b91d 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -32,7 +32,7 @@ struct notifier_block br_device_notifier = {
32static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) 32static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
33{ 33{
34 struct net_device *dev = ptr; 34 struct net_device *dev = ptr;
35 struct net_bridge_port *p = br_port_get(dev); 35 struct net_bridge_port *p;
36 struct net_bridge *br; 36 struct net_bridge *br;
37 int err; 37 int err;
38 38
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index b862071bf601..46e0bec1d7c5 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -151,11 +151,19 @@ struct net_bridge_port
151#endif 151#endif
152}; 152};
153 153
154#define br_port_get_rcu(dev) \
155 ((struct net_bridge_port *) rcu_dereference(dev->rx_handler_data))
156#define br_port_get(dev) ((struct net_bridge_port *) dev->rx_handler_data)
157#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT) 154#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
158 155
156static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev)
157{
158 struct net_bridge_port *port = rcu_dereference(dev->rx_handler_data);
159 return br_port_exists(dev) ? port : NULL;
160}
161
162static inline struct net_bridge_port *br_port_get(struct net_device *dev)
163{
164 return br_port_exists(dev) ? dev->rx_handler_data : NULL;
165}
166
159struct br_cpu_netstats { 167struct br_cpu_netstats {
160 u64 rx_packets; 168 u64 rx_packets;
161 u64 rx_bytes; 169 u64 rx_bytes;
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index 35cf27087b56..3d9a55d3822f 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -141,10 +141,6 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
141 struct net_bridge *br; 141 struct net_bridge *br;
142 const unsigned char *buf; 142 const unsigned char *buf;
143 143
144 if (!br_port_exists(dev))
145 goto err;
146 p = br_port_get_rcu(dev);
147
148 if (!pskb_may_pull(skb, 4)) 144 if (!pskb_may_pull(skb, 4))
149 goto err; 145 goto err;
150 146
@@ -153,6 +149,10 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
153 if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0) 149 if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
154 goto err; 150 goto err;
155 151
152 p = br_port_get_rcu(dev);
153 if (!p)
154 goto err;
155
156 br = p->br; 156 br = p->br;
157 spin_lock(&br->lock); 157 spin_lock(&br->lock);
158 158
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index a1dcf83f0d58..cbc9f395ab1e 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -128,6 +128,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
128 const struct net_device *in, const struct net_device *out) 128 const struct net_device *in, const struct net_device *out)
129{ 129{
130 const struct ethhdr *h = eth_hdr(skb); 130 const struct ethhdr *h = eth_hdr(skb);
131 const struct net_bridge_port *p;
131 __be16 ethproto; 132 __be16 ethproto;
132 int verdict, i; 133 int verdict, i;
133 134
@@ -148,13 +149,11 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
148 if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT)) 149 if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
149 return 1; 150 return 1;
150 /* rcu_read_lock()ed by nf_hook_slow */ 151 /* rcu_read_lock()ed by nf_hook_slow */
151 if (in && br_port_exists(in) && 152 if (in && (p = br_port_get_rcu(in)) != NULL &&
152 FWINV2(ebt_dev_check(e->logical_in, br_port_get_rcu(in)->br->dev), 153 FWINV2(ebt_dev_check(e->logical_in, p->br->dev), EBT_ILOGICALIN))
153 EBT_ILOGICALIN))
154 return 1; 154 return 1;
155 if (out && br_port_exists(out) && 155 if (out && (p = br_port_get_rcu(out)) != NULL &&
156 FWINV2(ebt_dev_check(e->logical_out, br_port_get_rcu(out)->br->dev), 156 FWINV2(ebt_dev_check(e->logical_out, p->br->dev), EBT_ILOGICALOUT))
157 EBT_ILOGICALOUT))
158 return 1; 157 return 1;
159 158
160 if (e->bitmask & EBT_SOURCEMAC) { 159 if (e->bitmask & EBT_SOURCEMAC) {