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:
Коммит
172947ac67
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче