Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b5ed54e9 authored by stephen hemminger's avatar stephen hemminger Committed by David S. Miller
Browse files

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: default avatarStephen Hemminger <shemminger@vyatta.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 61391cde
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -238,15 +238,18 @@ struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
{
	struct net_bridge_fdb_entry *fdb;
	struct net_bridge_port *port;
	int ret;

	if (!br_port_exists(dev))
		return 0;

	rcu_read_lock();
	fdb = __br_fdb_get(br_port_get_rcu(dev)->br, addr);
	port = br_port_get_rcu(dev);
	if (!port)
		ret = 0;
	else {
		fdb = __br_fdb_get(port->br, addr);
		ret = fdb && fdb->dst->dev != dev &&
			fdb->dst->state == BR_STATE_FORWARDING;
	}
	rcu_read_unlock();

	return ret;
+1 −4
Original line number Diff line number Diff line
@@ -475,11 +475,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
{
	struct net_bridge_port *p;

	if (!br_port_exists(dev))
		return -EINVAL;

	p = br_port_get(dev);
	if (p->br != br)
	if (!p || p->br != br)
		return -EINVAL;

	del_nbp(p);
+7 −6
Original line number Diff line number Diff line
@@ -131,17 +131,18 @@ void br_netfilter_rtable_init(struct net_bridge *br)

static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
{
	if (!br_port_exists(dev))
		return NULL;
	return &br_port_get_rcu(dev)->br->fake_rtable;
	struct net_bridge_port *port;

	port = br_port_get_rcu(dev);
	return port ? &port->br->fake_rtable : NULL;
}

static inline struct net_device *bridge_parent(const struct net_device *dev)
{
	if (!br_port_exists(dev))
		return NULL;
	struct net_bridge_port *port;

	return br_port_get_rcu(dev)->br->dev;
	port = br_port_get_rcu(dev);
	return port ? port->br->dev : NULL;
}

static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
+6 −4
Original line number Diff line number Diff line
@@ -119,11 +119,13 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)

	idx = 0;
	for_each_netdev(net, dev) {
		struct net_bridge_port *port = br_port_get(dev);

		/* not a bridge port */
		if (!br_port_exists(dev) || idx < cb->args[0])
		if (!port || idx < cb->args[0])
			goto skip;

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

	if (!br_port_exists(dev))
		return -EINVAL;
	p = br_port_get(dev);
	if (!p)
		return -EINVAL;

	/* if kernel STP is running, don't allow changes */
	if (p->br->stp_enabled == BR_KERNEL_STP)
+1 −1
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ struct notifier_block br_device_notifier = {
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
	struct net_device *dev = ptr;
	struct net_bridge_port *p = br_port_get(dev);
	struct net_bridge_port *p;
	struct net_bridge *br;
	int err;

Loading