Skip to content

Commit 24a6e55

Browse files
Nikolay AleksandrovSasha Levin
authored andcommitted
net: bridge: fix vlan tunnel dst null pointer dereference
commit 58e2071 upstream. 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]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 534fb8a commit 24a6e55

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
@@ -100,8 +100,8 @@ struct br_vlan_stats {
100100
};
101101

102102
struct br_tunnel_info {
103-
__be64 tunnel_id;
104-
struct metadata_dst *tunnel_dst;
103+
__be64 tunnel_id;
104+
struct metadata_dst __rcu *tunnel_dst;
105105
};
106106

107107
/**

net/bridge/br_vlan_tunnel.c

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,33 @@ static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
4646
br_vlan_tunnel_rht_params);
4747
}
4848

49+
static void vlan_tunnel_info_release(struct net_bridge_vlan *vlan)
50+
{
51+
struct metadata_dst *tdst = rtnl_dereference(vlan->tinfo.tunnel_dst);
52+
53+
WRITE_ONCE(vlan->tinfo.tunnel_id, 0);
54+
RCU_INIT_POINTER(vlan->tinfo.tunnel_dst, NULL);
55+
dst_release(&tdst->dst);
56+
}
57+
4958
void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
5059
struct net_bridge_vlan *vlan)
5160
{
52-
if (!vlan->tinfo.tunnel_dst)
61+
if (!rcu_access_pointer(vlan->tinfo.tunnel_dst))
5362
return;
5463
rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode,
5564
br_vlan_tunnel_rht_params);
56-
vlan->tinfo.tunnel_id = 0;
57-
dst_release(&vlan->tinfo.tunnel_dst->dst);
58-
vlan->tinfo.tunnel_dst = NULL;
65+
vlan_tunnel_info_release(vlan);
5966
}
6067

6168
static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
6269
struct net_bridge_vlan *vlan, u32 tun_id)
6370
{
64-
struct metadata_dst *metadata = NULL;
71+
struct metadata_dst *metadata = rtnl_dereference(vlan->tinfo.tunnel_dst);
6572
__be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id));
6673
int err;
6774

68-
if (vlan->tinfo.tunnel_dst)
75+
if (metadata)
6976
return -EEXIST;
7077

7178
metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY,
@@ -74,8 +81,8 @@ static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
7481
return -EINVAL;
7582

7683
metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
77-
vlan->tinfo.tunnel_dst = metadata;
78-
vlan->tinfo.tunnel_id = key;
84+
rcu_assign_pointer(vlan->tinfo.tunnel_dst, metadata);
85+
WRITE_ONCE(vlan->tinfo.tunnel_id, key);
7986

8087
err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
8188
br_vlan_tunnel_rht_params);
@@ -84,9 +91,7 @@ static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
8491

8592
return 0;
8693
out:
87-
dst_release(&vlan->tinfo.tunnel_dst->dst);
88-
vlan->tinfo.tunnel_dst = NULL;
89-
vlan->tinfo.tunnel_id = 0;
94+
vlan_tunnel_info_release(vlan);
9095

9196
return err;
9297
}
@@ -186,20 +191,25 @@ int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
186191
int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
187192
struct net_bridge_vlan *vlan)
188193
{
194+
struct metadata_dst *tunnel_dst;
195+
__be64 tunnel_id;
189196
int err;
190197

191-
if (!vlan || !vlan->tinfo.tunnel_id)
198+
if (!vlan)
192199
return 0;
193200

194-
if (unlikely(!skb_vlan_tag_present(skb)))
201+
tunnel_id = READ_ONCE(vlan->tinfo.tunnel_id);
202+
if (!tunnel_id || unlikely(!skb_vlan_tag_present(skb)))
195203
return 0;
196204

197205
skb_dst_drop(skb);
198206
err = skb_vlan_pop(skb);
199207
if (err)
200208
return err;
201209

202-
skb_dst_set(skb, dst_clone(&vlan->tinfo.tunnel_dst->dst));
210+
tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst);
211+
if (tunnel_dst)
212+
skb_dst_set(skb, dst_clone(&tunnel_dst->dst));
203213

204214
return 0;
205215
}

0 commit comments

Comments
 (0)