Skip to content

Commit b5ed54e

Browse files
stephen hemmingerdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 61391cd commit b5ed54e

File tree

8 files changed

+44
-34
lines changed

8 files changed

+44
-34
lines changed

net/bridge/br_fdb.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -238,15 +238,18 @@ struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
238238
int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
239239
{
240240
struct net_bridge_fdb_entry *fdb;
241+
struct net_bridge_port *port;
241242
int ret;
242243

243-
if (!br_port_exists(dev))
244-
return 0;
245-
246244
rcu_read_lock();
247-
fdb = __br_fdb_get(br_port_get_rcu(dev)->br, addr);
248-
ret = fdb && fdb->dst->dev != dev &&
249-
fdb->dst->state == BR_STATE_FORWARDING;
245+
port = br_port_get_rcu(dev);
246+
if (!port)
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+
}
250253
rcu_read_unlock();
251254

252255
return ret;

net/bridge/br_if.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -475,11 +475,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
475475
{
476476
struct net_bridge_port *p;
477477

478-
if (!br_port_exists(dev))
479-
return -EINVAL;
480-
481478
p = br_port_get(dev);
482-
if (p->br != br)
479+
if (!p || p->br != br)
483480
return -EINVAL;
484481

485482
del_nbp(p);

net/bridge/br_netfilter.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,18 @@ void br_netfilter_rtable_init(struct net_bridge *br)
131131

132132
static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
133133
{
134-
if (!br_port_exists(dev))
135-
return NULL;
136-
return &br_port_get_rcu(dev)->br->fake_rtable;
134+
struct net_bridge_port *port;
135+
136+
port = br_port_get_rcu(dev);
137+
return port ? &port->br->fake_rtable : NULL;
137138
}
138139

139140
static inline struct net_device *bridge_parent(const struct net_device *dev)
140141
{
141-
if (!br_port_exists(dev))
142-
return NULL;
142+
struct net_bridge_port *port;
143143

144-
return br_port_get_rcu(dev)->br->dev;
144+
port = br_port_get_rcu(dev);
145+
return port ? port->br->dev : NULL;
145146
}
146147

147148
static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)

net/bridge/br_netlink.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,13 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
119119

120120
idx = 0;
121121
for_each_netdev(net, dev) {
122+
struct net_bridge_port *port = br_port_get(dev);
123+
122124
/* not a bridge port */
123-
if (!br_port_exists(dev) || idx < cb->args[0])
125+
if (!port || idx < cb->args[0])
124126
goto skip;
125127

126-
if (br_fill_ifinfo(skb, br_port_get(dev),
128+
if (br_fill_ifinfo(skb, port,
127129
NETLINK_CB(cb->skb).pid,
128130
cb->nlh->nlmsg_seq, RTM_NEWLINK,
129131
NLM_F_MULTI) < 0)
@@ -169,9 +171,9 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
169171
if (!dev)
170172
return -ENODEV;
171173

172-
if (!br_port_exists(dev))
173-
return -EINVAL;
174174
p = br_port_get(dev);
175+
if (!p)
176+
return -EINVAL;
175177

176178
/* if kernel STP is running, don't allow changes */
177179
if (p->br->stp_enabled == BR_KERNEL_STP)

net/bridge/br_notify.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ struct notifier_block br_device_notifier = {
3232
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
3333
{
3434
struct net_device *dev = ptr;
35-
struct net_bridge_port *p = br_port_get(dev);
35+
struct net_bridge_port *p;
3636
struct net_bridge *br;
3737
int err;
3838

net/bridge/br_private.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,19 @@ struct net_bridge_port
151151
#endif
152152
};
153153

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)
157154
#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
158155

156+
static 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+
162+
static 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+
159167
struct br_cpu_netstats {
160168
u64 rx_packets;
161169
u64 rx_bytes;

net/bridge/br_stp_bpdu.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,6 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
141141
struct net_bridge *br;
142142
const unsigned char *buf;
143143

144-
if (!br_port_exists(dev))
145-
goto err;
146-
p = br_port_get_rcu(dev);
147-
148144
if (!pskb_may_pull(skb, 4))
149145
goto err;
150146

@@ -153,6 +149,10 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
153149
if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
154150
goto err;
155151

152+
p = br_port_get_rcu(dev);
153+
if (!p)
154+
goto err;
155+
156156
br = p->br;
157157
spin_lock(&br->lock);
158158

net/bridge/netfilter/ebtables.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
128128
const struct net_device *in, const struct net_device *out)
129129
{
130130
const struct ethhdr *h = eth_hdr(skb);
131+
const struct net_bridge_port *p;
131132
__be16 ethproto;
132133
int verdict, i;
133134

@@ -148,13 +149,11 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
148149
if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
149150
return 1;
150151
/* rcu_read_lock()ed by nf_hook_slow */
151-
if (in && br_port_exists(in) &&
152-
FWINV2(ebt_dev_check(e->logical_in, br_port_get_rcu(in)->br->dev),
153-
EBT_ILOGICALIN))
152+
if (in && (p = br_port_get_rcu(in)) != NULL &&
153+
FWINV2(ebt_dev_check(e->logical_in, p->br->dev), EBT_ILOGICALIN))
154154
return 1;
155-
if (out && br_port_exists(out) &&
156-
FWINV2(ebt_dev_check(e->logical_out, br_port_get_rcu(out)->br->dev),
157-
EBT_ILOGICALOUT))
155+
if (out && (p = br_port_get_rcu(out)) != NULL &&
156+
FWINV2(ebt_dev_check(e->logical_out, p->br->dev), EBT_ILOGICALOUT))
158157
return 1;
159158

160159
if (e->bitmask & EBT_SOURCEMAC) {

0 commit comments

Comments
 (0)