Skip to content

Commit 1a6fdb3

Browse files
committed
Merge branch 'bridge-vxlan-harden-nd-option-parsing-paths'
Yang Yang says: ==================== bridge/vxlan: harden ND option parsing paths This series hardens ND option parsing in bridge and vxlan paths. Patch 1 linearizes the request skb in br_nd_send() before walking ND options. Patch 2 adds explicit ND option length validation in br_nd_send(). Patch 3 adds matching ND option length validation in vxlan_na_create(). ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents c11c731 + afa9a05 commit 1a6fdb3

2 files changed

Lines changed: 15 additions & 9 deletions

File tree

drivers/net/vxlan/vxlan_core.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,12 +1965,14 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
19651965
ns_olen = request->len - skb_network_offset(request) -
19661966
sizeof(struct ipv6hdr) - sizeof(*ns);
19671967
for (i = 0; i < ns_olen-1; i += (ns->opt[i+1]<<3)) {
1968-
if (!ns->opt[i + 1]) {
1968+
if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
19691969
kfree_skb(reply);
19701970
return NULL;
19711971
}
19721972
if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
1973-
daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
1973+
if ((ns->opt[i + 1] << 3) >=
1974+
sizeof(struct nd_opt_hdr) + ETH_ALEN)
1975+
daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
19741976
break;
19751977
}
19761978
}

net/bridge/br_arp_nd_proxy.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -251,20 +251,20 @@ struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg)
251251

252252
static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
253253
struct sk_buff *request, struct neighbour *n,
254-
__be16 vlan_proto, u16 vlan_tci, struct nd_msg *ns)
254+
__be16 vlan_proto, u16 vlan_tci)
255255
{
256256
struct net_device *dev = request->dev;
257257
struct net_bridge_vlan_group *vg;
258+
struct nd_msg *na, *ns;
258259
struct sk_buff *reply;
259-
struct nd_msg *na;
260260
struct ipv6hdr *pip6;
261261
int na_olen = 8; /* opt hdr + ETH_ALEN for target */
262262
int ns_olen;
263263
int i, len;
264264
u8 *daddr;
265265
u16 pvid;
266266

267-
if (!dev)
267+
if (!dev || skb_linearize(request))
268268
return;
269269

270270
len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
@@ -281,17 +281,21 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
281281
skb_set_mac_header(reply, 0);
282282

283283
daddr = eth_hdr(request)->h_source;
284+
ns = (struct nd_msg *)(skb_network_header(request) +
285+
sizeof(struct ipv6hdr));
284286

285287
/* Do we need option processing ? */
286288
ns_olen = request->len - (skb_network_offset(request) +
287289
sizeof(struct ipv6hdr)) - sizeof(*ns);
288290
for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
289-
if (!ns->opt[i + 1]) {
291+
if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
290292
kfree_skb(reply);
291293
return;
292294
}
293295
if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
294-
daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
296+
if ((ns->opt[i + 1] << 3) >=
297+
sizeof(struct nd_opt_hdr) + ETH_ALEN)
298+
daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
295299
break;
296300
}
297301
}
@@ -472,9 +476,9 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
472476
if (vid != 0)
473477
br_nd_send(br, p, skb, n,
474478
skb->vlan_proto,
475-
skb_vlan_tag_get(skb), msg);
479+
skb_vlan_tag_get(skb));
476480
else
477-
br_nd_send(br, p, skb, n, 0, 0, msg);
481+
br_nd_send(br, p, skb, n, 0, 0);
478482
replied = true;
479483
}
480484

0 commit comments

Comments
 (0)