Commit 1a6fdb35 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

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/20260326034441.2037420-1-n05ec@lzu.edu.cn


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents c11c731a afa9a05e
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1965,11 +1965,13 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
	ns_olen = request->len - skb_network_offset(request) -
		sizeof(struct ipv6hdr) - sizeof(*ns);
	for (i = 0; i < ns_olen-1; i += (ns->opt[i+1]<<3)) {
		if (!ns->opt[i + 1]) {
		if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
			kfree_skb(reply);
			return NULL;
		}
		if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
			if ((ns->opt[i + 1] << 3) >=
			    sizeof(struct nd_opt_hdr) + ETH_ALEN)
				daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
			break;
		}
+11 −7
Original line number Diff line number Diff line
@@ -251,12 +251,12 @@ struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg)

static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
		       struct sk_buff *request, struct neighbour *n,
		       __be16 vlan_proto, u16 vlan_tci, struct nd_msg *ns)
		       __be16 vlan_proto, u16 vlan_tci)
{
	struct net_device *dev = request->dev;
	struct net_bridge_vlan_group *vg;
	struct nd_msg *na, *ns;
	struct sk_buff *reply;
	struct nd_msg *na;
	struct ipv6hdr *pip6;
	int na_olen = 8; /* opt hdr + ETH_ALEN for target */
	int ns_olen;
@@ -264,7 +264,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
	u8 *daddr;
	u16 pvid;

	if (!dev)
	if (!dev || skb_linearize(request))
		return;

	len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
@@ -281,16 +281,20 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
	skb_set_mac_header(reply, 0);

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

	/* Do we need option processing ? */
	ns_olen = request->len - (skb_network_offset(request) +
				  sizeof(struct ipv6hdr)) - sizeof(*ns);
	for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
		if (!ns->opt[i + 1]) {
		if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
			kfree_skb(reply);
			return;
		}
		if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
			if ((ns->opt[i + 1] << 3) >=
			    sizeof(struct nd_opt_hdr) + ETH_ALEN)
				daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
			break;
		}
@@ -472,9 +476,9 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
				if (vid != 0)
					br_nd_send(br, p, skb, n,
						   skb->vlan_proto,
						   skb_vlan_tag_get(skb), msg);
						   skb_vlan_tag_get(skb));
				else
					br_nd_send(br, p, skb, n, 0, 0, msg);
					br_nd_send(br, p, skb, n, 0, 0);
				replied = true;
			}