Skip to content

Commit 58e2071

Browse files
Nikolay Aleksandrovdavem330
authored andcommitted
net: bridge: fix vlan tunnel dst null pointer dereference
This patch fixes a tunnel_dst null pointer dereference due to lockless access in the tunnel egress path. When deleting a vlan tunnel the tunnel_dst pointer is set to NULL without waiting a grace period (i.e. while it's still usable) and packets egressing are dereferencing it without checking. Use READ/WRITE_ONCE to annotate the lockless use of tunnel_id, use RCU for accessing tunnel_dst and make sure it is read only once and checked in the egress path. The dst is already properly RCU protected so we don't need to do anything fancy than to make sure tunnel_id and tunnel_dst are read only once and checked in the egress path. Cc: [email protected] Fixes: 11538d0 ("bridge: vlan dst_metadata hooks in ingress and egress paths") Signed-off-by: Nikolay Aleksandrov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9d44fa3 commit 58e2071

File tree

2 files changed

+26
-16
lines changed

2 files changed

+26
-16
lines changed

net/bridge/br_private.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ struct bridge_mcast_stats {
9090
#endif
9191

9292
struct br_tunnel_info {
93-
__be64 tunnel_id;
94-
struct metadata_dst *tunnel_dst;
93+
__be64 tunnel_id;
94+
struct metadata_dst __rcu *tunnel_dst;
9595
};
9696

9797
/* private vlan flags */

net/bridge/br_vlan_tunnel.c

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,33 @@ static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
4141
br_vlan_tunnel_rht_params);
4242
}
4343

44+
static void vlan_tunnel_info_release(struct net_bridge_vlan *vlan)
45+
{
46+
struct metadata_dst *tdst = rtnl_dereference(vlan->tinfo.tunnel_dst);
47+
48+
WRITE_ONCE(vlan->tinfo.tunnel_id, 0);
49+
RCU_INIT_POINTER(vlan->tinfo.tunnel_dst, NULL);
50+
dst_release(&tdst->dst);
51+
}
52+
4453
void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
4554
struct net_bridge_vlan *vlan)
4655
{
47-
if (!vlan->tinfo.tunnel_dst)
56+
if (!rcu_access_pointer(vlan->tinfo.tunnel_dst))
4857
return;
4958
rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode,
5059
br_vlan_tunnel_rht_params);
51-
vlan->tinfo.tunnel_id = 0;
52-
dst_release(&vlan->tinfo.tunnel_dst->dst);
53-
vlan->tinfo.tunnel_dst = NULL;
60+
vlan_tunnel_info_release(vlan);
5461
}
5562

5663
static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
5764
struct net_bridge_vlan *vlan, u32 tun_id)
5865
{
59-
struct metadata_dst *metadata = NULL;
66+
struct metadata_dst *metadata = rtnl_dereference(vlan->tinfo.tunnel_dst);
6067
__be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id));
6168
int err;
6269

63-
if (vlan->tinfo.tunnel_dst)
70+
if (metadata)
6471
return -EEXIST;
6572

6673
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,
6976
return -EINVAL;
7077

7178
metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
72-
vlan->tinfo.tunnel_dst = metadata;
73-
vlan->tinfo.tunnel_id = key;
79+
rcu_assign_pointer(vlan->tinfo.tunnel_dst, metadata);
80+
WRITE_ONCE(vlan->tinfo.tunnel_id, key);
7481

7582
err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
7683
br_vlan_tunnel_rht_params);
@@ -79,9 +86,7 @@ static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
7986

8087
return 0;
8188
out:
82-
dst_release(&vlan->tinfo.tunnel_dst->dst);
83-
vlan->tinfo.tunnel_dst = NULL;
84-
vlan->tinfo.tunnel_id = 0;
89+
vlan_tunnel_info_release(vlan);
8590

8691
return err;
8792
}
@@ -182,20 +187,25 @@ int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
182187
int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
183188
struct net_bridge_vlan *vlan)
184189
{
190+
struct metadata_dst *tunnel_dst;
191+
__be64 tunnel_id;
185192
int err;
186193

187-
if (!vlan || !vlan->tinfo.tunnel_id)
194+
if (!vlan)
188195
return 0;
189196

190-
if (unlikely(!skb_vlan_tag_present(skb)))
197+
tunnel_id = READ_ONCE(vlan->tinfo.tunnel_id);
198+
if (!tunnel_id || unlikely(!skb_vlan_tag_present(skb)))
191199
return 0;
192200

193201
skb_dst_drop(skb);
194202
err = skb_vlan_pop(skb);
195203
if (err)
196204
return err;
197205

198-
skb_dst_set(skb, dst_clone(&vlan->tinfo.tunnel_dst->dst));
206+
tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst);
207+
if (tunnel_dst)
208+
skb_dst_set(skb, dst_clone(&tunnel_dst->dst));
199209

200210
return 0;
201211
}

0 commit comments

Comments
 (0)