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: 11538d039a ("bridge: vlan dst_metadata hooks in ingress and egress paths")

v2: no changes, added stable list to CC
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2021-06-10 14:06:43 -07:00
Родитель 9d44fa3e50 cfc579f9d8
Коммит 172947ac67
2 изменённых файлов: 26 добавлений и 16 удалений

Просмотреть файл

@ -90,8 +90,8 @@ struct bridge_mcast_stats {
#endif
struct br_tunnel_info {
__be64 tunnel_id;
struct metadata_dst *tunnel_dst;
__be64 tunnel_id;
struct metadata_dst __rcu *tunnel_dst;
};
/* private vlan flags */

Просмотреть файл

@ -41,26 +41,33 @@ static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
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,
struct net_bridge_vlan *vlan)
{
if (!vlan->tinfo.tunnel_dst)
if (!rcu_access_pointer(vlan->tinfo.tunnel_dst))
return;
rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode,
br_vlan_tunnel_rht_params);
vlan->tinfo.tunnel_id = 0;
dst_release(&vlan->tinfo.tunnel_dst->dst);
vlan->tinfo.tunnel_dst = NULL;
vlan_tunnel_info_release(vlan);
}
static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
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));
int err;
if (vlan->tinfo.tunnel_dst)
if (metadata)
return -EEXIST;
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,
return -EINVAL;
metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
vlan->tinfo.tunnel_dst = metadata;
vlan->tinfo.tunnel_id = key;
rcu_assign_pointer(vlan->tinfo.tunnel_dst, metadata);
WRITE_ONCE(vlan->tinfo.tunnel_id, key);
err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
br_vlan_tunnel_rht_params);
@ -79,9 +86,7 @@ static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
return 0;
out:
dst_release(&vlan->tinfo.tunnel_dst->dst);
vlan->tinfo.tunnel_dst = NULL;
vlan->tinfo.tunnel_id = 0;
vlan_tunnel_info_release(vlan);
return err;
}
@ -182,12 +187,15 @@ int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
struct net_bridge_vlan *vlan)
{
struct metadata_dst *tunnel_dst;
__be64 tunnel_id;
int err;
if (!vlan || !vlan->tinfo.tunnel_id)
if (!vlan)
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;
skb_dst_drop(skb);
@ -195,7 +203,9 @@ int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
if (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;
}