Commit 172947ac authored by David S. Miller's avatar David S. Miller

Merge branch 'bridge-egress-fixes'

Nikolay Aleksandrov says:

====================
net: bridge: vlan tunnel egress path fixes

These two fixes take care of tunnel_dst problems in the vlan tunnel egress
path. Patch 01 fixes a null ptr deref due to the lockless use of tunnel_dst
pointer without checking it first, and patch 02 fixes a use-after-free
issue due to wrong dst refcounting (dst_clone() -> dst_hold_safe()).

Both fix the same commit and should be queued for stable backports:
Fixes: 11538d03 ("bridge: vlan dst_metadata hooks in ingress and egress paths")

v2: no changes, added stable list to CC
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9d44fa3e cfc579f9
...@@ -90,8 +90,8 @@ struct bridge_mcast_stats { ...@@ -90,8 +90,8 @@ struct bridge_mcast_stats {
#endif #endif
struct br_tunnel_info { struct br_tunnel_info {
__be64 tunnel_id; __be64 tunnel_id;
struct metadata_dst *tunnel_dst; struct metadata_dst __rcu *tunnel_dst;
}; };
/* private vlan flags */ /* private vlan flags */
......
...@@ -41,26 +41,33 @@ static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl, ...@@ -41,26 +41,33 @@ static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
br_vlan_tunnel_rht_params); br_vlan_tunnel_rht_params);
} }
static void vlan_tunnel_info_release(struct net_bridge_vlan *vlan)
{
struct metadata_dst *tdst = rtnl_dereference(vlan->tinfo.tunnel_dst);
WRITE_ONCE(vlan->tinfo.tunnel_id, 0);
RCU_INIT_POINTER(vlan->tinfo.tunnel_dst, NULL);
dst_release(&tdst->dst);
}
void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg, void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *vlan) struct net_bridge_vlan *vlan)
{ {
if (!vlan->tinfo.tunnel_dst) if (!rcu_access_pointer(vlan->tinfo.tunnel_dst))
return; return;
rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode, rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode,
br_vlan_tunnel_rht_params); br_vlan_tunnel_rht_params);
vlan->tinfo.tunnel_id = 0; vlan_tunnel_info_release(vlan);
dst_release(&vlan->tinfo.tunnel_dst->dst);
vlan->tinfo.tunnel_dst = NULL;
} }
static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg, static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *vlan, u32 tun_id) struct net_bridge_vlan *vlan, u32 tun_id)
{ {
struct metadata_dst *metadata = NULL; struct metadata_dst *metadata = rtnl_dereference(vlan->tinfo.tunnel_dst);
__be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id)); __be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id));
int err; int err;
if (vlan->tinfo.tunnel_dst) if (metadata)
return -EEXIST; return -EEXIST;
metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY, metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY,
...@@ -69,8 +76,8 @@ static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg, ...@@ -69,8 +76,8 @@ static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
return -EINVAL; return -EINVAL;
metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE; metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
vlan->tinfo.tunnel_dst = metadata; rcu_assign_pointer(vlan->tinfo.tunnel_dst, metadata);
vlan->tinfo.tunnel_id = key; WRITE_ONCE(vlan->tinfo.tunnel_id, key);
err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode, err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
br_vlan_tunnel_rht_params); br_vlan_tunnel_rht_params);
...@@ -79,9 +86,7 @@ static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg, ...@@ -79,9 +86,7 @@ static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
return 0; return 0;
out: out:
dst_release(&vlan->tinfo.tunnel_dst->dst); vlan_tunnel_info_release(vlan);
vlan->tinfo.tunnel_dst = NULL;
vlan->tinfo.tunnel_id = 0;
return err; return err;
} }
...@@ -182,12 +187,15 @@ int br_handle_ingress_vlan_tunnel(struct sk_buff *skb, ...@@ -182,12 +187,15 @@ int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
int br_handle_egress_vlan_tunnel(struct sk_buff *skb, int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
struct net_bridge_vlan *vlan) struct net_bridge_vlan *vlan)
{ {
struct metadata_dst *tunnel_dst;
__be64 tunnel_id;
int err; int err;
if (!vlan || !vlan->tinfo.tunnel_id) if (!vlan)
return 0; return 0;
if (unlikely(!skb_vlan_tag_present(skb))) tunnel_id = READ_ONCE(vlan->tinfo.tunnel_id);
if (!tunnel_id || unlikely(!skb_vlan_tag_present(skb)))
return 0; return 0;
skb_dst_drop(skb); skb_dst_drop(skb);
...@@ -195,7 +203,9 @@ int br_handle_egress_vlan_tunnel(struct sk_buff *skb, ...@@ -195,7 +203,9 @@ int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
if (err) if (err)
return err; return err;
skb_dst_set(skb, dst_clone(&vlan->tinfo.tunnel_dst->dst)); tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst);
if (tunnel_dst && dst_hold_safe(&tunnel_dst->dst))
skb_dst_set(skb, &tunnel_dst->dst);
return 0; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment