summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolay Aleksandrov <razor@blackwall.org>2015-06-15 20:28:51 +0300
committerZefan Li <lizefan@huawei.com>2015-10-22 09:20:05 +0800
commit9beee8b18a53aaf9a9678245dd1687a0430d862b (patch)
tree22348750349ae7ebc9074cbea30dcea6f2b27d1f
parentaef2b5342c1c17abbcd068d25ce5c48e6b43a5f8 (diff)
bridge: fix br_stp_set_bridge_priority race conditions
commit 2dab80a8b486f02222a69daca6859519e05781d9 upstream. After the ->set() spinlocks were removed br_stp_set_bridge_priority was left running without any protection when used via sysfs. It can race with port add/del and could result in use-after-free cases and corrupted lists. Tested by running port add/del in a loop with stp enabled while setting priority in a loop, crashes are easily reproducible. The spinlocks around sysfs ->set() were removed in commit: 14f98f258f19 ("bridge: range check STP parameters") There's also a race condition in the netlink priority support that is fixed by this change, but it was introduced recently and the fixes tag covers it, just in case it's needed the commit is: af615762e972 ("bridge: add ageing_time, stp_state, priority over netlink") Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org> Fixes: 14f98f258f19 ("bridge: range check STP parameters") Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Zefan Li <lizefan@huawei.com>
-rw-r--r--net/bridge/br_ioctl.c2
-rw-r--r--net/bridge/br_stp_if.c4
2 files changed, 3 insertions, 3 deletions
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 7222fe1d5460..ea0e15c7ea17 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -246,9 +246,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- spin_lock_bh(&br->lock);
br_stp_set_bridge_priority(br, args[1]);
- spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_PORT_PRIORITY:
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 2f100ccef86f..23ea15989d55 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -242,12 +242,13 @@ bool br_stp_recalculate_bridge_id(struct net_bridge *br)
return true;
}
-/* called under bridge lock */
+/* Acquires and releases bridge lock */
void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
{
struct net_bridge_port *p;
int wasroot;
+ spin_lock_bh(&br->lock);
wasroot = br_is_root_bridge(br);
list_for_each_entry(p, &br->port_list, list) {
@@ -265,6 +266,7 @@ void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
br_port_state_selection(br);
if (br_is_root_bridge(br) && !wasroot)
br_become_root_bridge(br);
+ spin_unlock_bh(&br->lock);
}
/* called under bridge lock */