diff options
author | Nikolay Aleksandrov <nikolay@cumulusnetworks.com> | 2018-11-16 18:50:01 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-11-17 21:38:44 -0800 |
commit | 9d332e69c1dc74dcd748de7cbd2dac5c61bda265 (patch) | |
tree | 912c15036f8f473ddbf7526c4c9b4763c18f164f /net/bridge/br_vlan.c | |
parent | 95506588d2c1d72ca29adef8ae9bf771bcfb4ced (diff) | |
download | linux-9d332e69c1dc74dcd748de7cbd2dac5c61bda265.tar.bz2 |
net: bridge: fix vlan stats use-after-free on destruction
Syzbot reported a use-after-free of the global vlan context on port vlan
destruction. When I added per-port vlan stats I missed the fact that the
global vlan context can be freed before the per-port vlan rcu callback.
There're a few different ways to deal with this, I've chosen to add a
new private flag that is set only when per-port stats are allocated so
we can directly check it on destruction without dereferencing the global
context at all. The new field in net_bridge_vlan uses a hole.
v2: cosmetic change, move the check to br_process_vlan_info where the
other checks are done
v3: add change log in the patch, add private (in-kernel only) flags in a
hole in net_bridge_vlan struct and use that instead of mixing
user-space flags with private flags
Fixes: 9163a0fc1f0c ("net: bridge: add support for per-port vlan stats")
Reported-by: syzbot+04681da557a0e49a52e5@syzkaller.appspotmail.com
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_vlan.c')
-rw-r--r-- | net/bridge/br_vlan.c | 3 |
1 files changed, 2 insertions, 1 deletions
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 8c9297a01947..e84be08b8285 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -197,7 +197,7 @@ static void nbp_vlan_rcu_free(struct rcu_head *rcu) v = container_of(rcu, struct net_bridge_vlan, rcu); WARN_ON(br_vlan_is_master(v)); /* if we had per-port stats configured then free them here */ - if (v->brvlan->stats != v->stats) + if (v->priv_flags & BR_VLFLAG_PER_PORT_STATS) free_percpu(v->stats); v->stats = NULL; kfree(v); @@ -264,6 +264,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) err = -ENOMEM; goto out_filt; } + v->priv_flags |= BR_VLFLAG_PER_PORT_STATS; } else { v->stats = masterv->stats; } |