naccyde / rpms / iproute

Forked from rpms/iproute 7 months ago
Clone

Blame SOURCES/0051-ip-route-Fix-segfault-with-many-nexthops.patch

36cfb7
From 5845a145808162560293cf4f7c55bbb5afc8dce7 Mon Sep 17 00:00:00 2001
36cfb7
From: Phil Sutter <psutter@redhat.com>
36cfb7
Date: Thu, 21 Feb 2019 14:38:57 +0100
36cfb7
Subject: [PATCH] ip-route: Fix segfault with many nexthops
36cfb7
36cfb7
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1624656
36cfb7
Upstream Status: iproute2.git commit bd59e5b1517b0
36cfb7
Conflicts: Context changes due to missing other commits.
36cfb7
36cfb7
commit bd59e5b1517b09b6f26d59f38fe6077d953c2396
36cfb7
Author: Phil Sutter <phil@nwl.cc>
36cfb7
Date:   Thu Sep 6 15:31:51 2018 +0200
36cfb7
36cfb7
    ip-route: Fix segfault with many nexthops
36cfb7
36cfb7
    It was possible to crash ip-route by adding an IPv6 route with 37
36cfb7
    nexthop statements. A simple reproducer is:
36cfb7
36cfb7
    | for i in `seq 37`; do
36cfb7
    |       nhs="nexthop via 1111::$i "$nhs
36cfb7
    | done
36cfb7
    | ip -6 route add 3333::/64 $nhs
36cfb7
36cfb7
    The related code was broken in multiple ways:
36cfb7
36cfb7
    * parse_one_nh() assumed that rta points to 4kB of storage but caller
36cfb7
      provided just 1kB. Fixed by passing 'len' parameter with the correct
36cfb7
      value.
36cfb7
36cfb7
    * Error checking of rta_addattr*() calls in parse_one_nh() and called
36cfb7
      functions was completely absent, so with above fix in place output
36cfb7
      flood would occur due to parser looping forever.
36cfb7
36cfb7
    While being at it, increase message buffer sizes to 4k. This allows for
36cfb7
    at most 144 nexthops.
36cfb7
36cfb7
    Signed-off-by: Phil Sutter <phil@nwl.cc>
36cfb7
    Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
36cfb7
---
e138d9
 ip/iproute.c          | 43 +++++++++++++++++------------
e138d9
 ip/iproute_lwtunnel.c | 63 +++++++++++++++++++++++++++----------------
36cfb7
 2 files changed, 66 insertions(+), 40 deletions(-)
36cfb7
36cfb7
diff --git a/ip/iproute.c b/ip/iproute.c
e138d9
index 759032db454ad..d4db035fc7b24 100644
36cfb7
--- a/ip/iproute.c
36cfb7
+++ b/ip/iproute.c
36cfb7
@@ -721,7 +721,7 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
36cfb7
 }
36cfb7
 
36cfb7
 static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
36cfb7
-			struct rtattr *rta, struct rtnexthop *rtnh,
36cfb7
+			struct rtattr *rta, size_t len, struct rtnexthop *rtnh,
36cfb7
 			int *argcp, char ***argvp)
36cfb7
 {
36cfb7
 	int argc = *argcp;
36cfb7
@@ -742,11 +742,16 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
36cfb7
 			if (r->rtm_family == AF_UNSPEC)
36cfb7
 				r->rtm_family = addr.family;
36cfb7
 			if (addr.family == r->rtm_family) {
36cfb7
-				rta_addattr_l(rta, 4096, RTA_GATEWAY, &addr.data, addr.bytelen);
36cfb7
-				rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen;
36cfb7
+				if (rta_addattr_l(rta, len, RTA_GATEWAY,
36cfb7
+						  &addr.data, addr.bytelen))
36cfb7
+					return -1;
36cfb7
+				rtnh->rtnh_len += sizeof(struct rtattr)
36cfb7
+						  + addr.bytelen;
36cfb7
 			} else {
36cfb7
-				rta_addattr_l(rta, 4096, RTA_VIA, &addr.family, addr.bytelen+2);
36cfb7
-				rtnh->rtnh_len += RTA_SPACE(addr.bytelen+2);
36cfb7
+				if (rta_addattr_l(rta, len, RTA_VIA,
36cfb7
+						  &addr.family, addr.bytelen + 2))
36cfb7
+					return -1;
36cfb7
+				rtnh->rtnh_len += RTA_SPACE(addr.bytelen + 2);
36cfb7
 			}
36cfb7
 		} else if (strcmp(*argv, "dev") == 0) {
36cfb7
 			NEXT_ARG();
36cfb7
@@ -769,13 +774,15 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
36cfb7
 			NEXT_ARG();
36cfb7
 			if (get_rt_realms_or_raw(&realm, *argv))
36cfb7
 				invarg("\"realm\" value is invalid\n", *argv);
36cfb7
-			rta_addattr32(rta, 4096, RTA_FLOW, realm);
36cfb7
+			if (rta_addattr32(rta, len, RTA_FLOW, realm))
36cfb7
+				return -1;
36cfb7
 			rtnh->rtnh_len += sizeof(struct rtattr) + 4;
36cfb7
 		} else if (strcmp(*argv, "encap") == 0) {
36cfb7
-			int len = rta->rta_len;
36cfb7
+			int old_len = rta->rta_len;
36cfb7
 
36cfb7
-			lwt_parse_encap(rta, 4096, &argc, &argv);
36cfb7
-			rtnh->rtnh_len += rta->rta_len - len;
36cfb7
+			if (lwt_parse_encap(rta, len, &argc, &argv))
36cfb7
+				return -1;
36cfb7
+			rtnh->rtnh_len += rta->rta_len - old_len;
36cfb7
 		} else if (strcmp(*argv, "as") == 0) {
36cfb7
 			inet_prefix addr;
36cfb7
 
36cfb7
@@ -783,8 +790,9 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
36cfb7
 			if (strcmp(*argv, "to") == 0)
36cfb7
 				NEXT_ARG();
36cfb7
 			get_addr(&addr, *argv, r->rtm_family);
36cfb7
-			rta_addattr_l(rta, 4096, RTA_NEWDST, &addr.data,
36cfb7
-				      addr.bytelen);
36cfb7
+			if (rta_addattr_l(rta, len, RTA_NEWDST,
36cfb7
+					  &addr.data, addr.bytelen))
36cfb7
+				return -1;
36cfb7
 			rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen;
36cfb7
 		} else
36cfb7
 			break;
36cfb7
@@ -797,7 +805,7 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
36cfb7
 static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r,
36cfb7
 			  int argc, char **argv)
36cfb7
 {
36cfb7
-	char buf[1024];
36cfb7
+	char buf[4096];
36cfb7
 	struct rtattr *rta = (void *)buf;
36cfb7
 	struct rtnexthop *rtnh;
36cfb7
 
36cfb7
@@ -817,7 +825,7 @@ static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r,
36cfb7
 		memset(rtnh, 0, sizeof(*rtnh));
36cfb7
 		rtnh->rtnh_len = sizeof(*rtnh);
36cfb7
 		rta->rta_len += rtnh->rtnh_len;
36cfb7
-		if (parse_one_nh(n, r, rta, rtnh, &argc, &argv)) {
36cfb7
+		if (parse_one_nh(n, r, rta, 4096, rtnh, &argc, &argv)) {
36cfb7
 			fprintf(stderr, "Error: cannot parse nexthop\n");
36cfb7
 			exit(-1);
36cfb7
 		}
36cfb7
@@ -825,7 +833,8 @@ static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r,
36cfb7
 	}
36cfb7
 
36cfb7
 	if (rta->rta_len > RTA_LENGTH(0))
36cfb7
-		addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
36cfb7
+		return addattr_l(n, 4096, RTA_MULTIPATH,
36cfb7
+				 RTA_DATA(rta), RTA_PAYLOAD(rta));
36cfb7
 	return 0;
36cfb7
 }
36cfb7
 
36cfb7
@@ -834,7 +843,7 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv)
36cfb7
 	struct {
36cfb7
 		struct nlmsghdr	n;
36cfb7
 		struct rtmsg		r;
36cfb7
-		char			buf[1024];
36cfb7
+		char			buf[4096];
36cfb7
 	} req = {
36cfb7
 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
36cfb7
 		.n.nlmsg_flags = NLM_F_REQUEST | flags,
36cfb7
@@ -1238,8 +1247,8 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv)
36cfb7
 		addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
36cfb7
 	}
36cfb7
 
36cfb7
-	if (nhs_ok)
36cfb7
-		parse_nexthops(&req.n, &req.r, argc, argv);
36cfb7
+	if (nhs_ok && parse_nexthops(&req.n, &req.r, argc, argv))
36cfb7
+		return -1;
36cfb7
 
36cfb7
 	if (req.r.rtm_family == AF_UNSPEC)
36cfb7
 		req.r.rtm_family = AF_INET;
36cfb7
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
e138d9
index 0fa1cab0a790f..1a6891267d2e1 100644
36cfb7
--- a/ip/iproute_lwtunnel.c
36cfb7
+++ b/ip/iproute_lwtunnel.c
36cfb7
@@ -255,8 +255,9 @@ static int parse_encap_mpls(struct rtattr *rta, size_t len,
36cfb7
 		exit(1);
36cfb7
 	}
36cfb7
 
36cfb7
-	rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, &addr.data,
36cfb7
-		      addr.bytelen);
36cfb7
+	if (rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST,
36cfb7
+			  &addr.data, addr.bytelen))
36cfb7
+		return -1;
36cfb7
 
36cfb7
 	*argcp = argc;
36cfb7
 	*argvp = argv;
36cfb7
@@ -270,6 +271,7 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
36cfb7
 	int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0;
36cfb7
 	char **argv = *argvp;
36cfb7
 	int argc = *argcp;
36cfb7
+	int ret = 0;
36cfb7
 
36cfb7
 	while (argc > 0) {
36cfb7
 		if (strcmp(*argv, "id") == 0) {
36cfb7
@@ -280,7 +282,7 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
36cfb7
 				duparg2("id", *argv);
36cfb7
 			if (get_be64(&id, *argv, 0))
36cfb7
 				invarg("\"id\" value is invalid\n", *argv);
36cfb7
-			rta_addattr64(rta, len, LWTUNNEL_IP_ID, id);
36cfb7
+			ret = rta_addattr64(rta, len, LWTUNNEL_IP_ID, id);
36cfb7
 		} else if (strcmp(*argv, "dst") == 0) {
36cfb7
 			inet_prefix addr;
36cfb7
 
36cfb7
@@ -288,8 +290,8 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
36cfb7
 			if (dst_ok++)
36cfb7
 				duparg2("dst", *argv);
36cfb7
 			get_addr(&addr, *argv, AF_INET);
36cfb7
-			rta_addattr_l(rta, len, LWTUNNEL_IP_DST,
36cfb7
-				      &addr.data, addr.bytelen);
36cfb7
+			ret = rta_addattr_l(rta, len, LWTUNNEL_IP_DST,
36cfb7
+					    &addr.data, addr.bytelen);
36cfb7
 		} else if (strcmp(*argv, "tos") == 0) {
36cfb7
 			__u32 tos;
36cfb7
 
36cfb7
@@ -298,7 +300,7 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
36cfb7
 				duparg2("tos", *argv);
36cfb7
 			if (rtnl_dsfield_a2n(&tos, *argv))
36cfb7
 				invarg("\"tos\" value is invalid\n", *argv);
36cfb7
-			rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos);
36cfb7
+			ret = rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos);
36cfb7
 		} else if (strcmp(*argv, "ttl") == 0) {
36cfb7
 			__u8 ttl;
36cfb7
 
36cfb7
@@ -307,10 +309,12 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
36cfb7
 				duparg2("ttl", *argv);
36cfb7
 			if (get_u8(&ttl, *argv, 0))
36cfb7
 				invarg("\"ttl\" value is invalid\n", *argv);
36cfb7
-			rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
36cfb7
+			ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
36cfb7
 		} else {
36cfb7
 			break;
36cfb7
 		}
36cfb7
+		if (ret)
36cfb7
+			break;
36cfb7
 		argc--; argv++;
36cfb7
 	}
36cfb7
 
36cfb7
@@ -321,7 +325,7 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
36cfb7
 	*argcp = argc + 1;
36cfb7
 	*argvp = argv - 1;
36cfb7
 
36cfb7
-	return 0;
36cfb7
+	return ret;
36cfb7
 }
36cfb7
 
36cfb7
 static int parse_encap_ila(struct rtattr *rta, size_t len,
36cfb7
@@ -330,6 +334,7 @@ static int parse_encap_ila(struct rtattr *rta, size_t len,
36cfb7
 	__u64 locator;
36cfb7
 	int argc = *argcp;
36cfb7
 	char **argv = *argvp;
36cfb7
+	int ret = 0;
36cfb7
 
36cfb7
 	if (get_addr64(&locator, *argv) < 0) {
36cfb7
 		fprintf(stderr, "Bad locator: %s\n", *argv);
36cfb7
@@ -338,7 +343,8 @@ static int parse_encap_ila(struct rtattr *rta, size_t len,
36cfb7
 
36cfb7
 	argc--; argv++;
36cfb7
 
36cfb7
-	rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator);
36cfb7
+	if (rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator))
36cfb7
+		return -1;
36cfb7
 
36cfb7
 	while (argc > 0) {
36cfb7
 		if (strcmp(*argv, "csum-mode") == 0) {
36cfb7
@@ -351,12 +357,15 @@ static int parse_encap_ila(struct rtattr *rta, size_t len,
36cfb7
 				invarg("\"csum-mode\" value is invalid\n",
36cfb7
 				       *argv);
36cfb7
 
36cfb7
-			rta_addattr8(rta, 1024, ILA_ATTR_CSUM_MODE, csum_mode);
36cfb7
+			ret = rta_addattr8(rta, 1024, ILA_ATTR_CSUM_MODE,
36cfb7
+					   (__u8)csum_mode);
36cfb7
 
36cfb7
 			argc--; argv++;
36cfb7
 		} else {
36cfb7
 			break;
36cfb7
 		}
36cfb7
+		if (ret)
36cfb7
+			break;
36cfb7
 	}
36cfb7
 
36cfb7
 	/* argv is currently the first unparsed argument,
36cfb7
@@ -366,7 +375,7 @@ static int parse_encap_ila(struct rtattr *rta, size_t len,
36cfb7
 	*argcp = argc + 1;
36cfb7
 	*argvp = argv - 1;
36cfb7
 
36cfb7
-	return 0;
36cfb7
+	return ret;
36cfb7
 }
36cfb7
 
36cfb7
 static int parse_encap_ip6(struct rtattr *rta, size_t len,
36cfb7
@@ -375,6 +384,7 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
36cfb7
 	int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0;
36cfb7
 	char **argv = *argvp;
36cfb7
 	int argc = *argcp;
36cfb7
+	int ret = 0;
36cfb7
 
36cfb7
 	while (argc > 0) {
36cfb7
 		if (strcmp(*argv, "id") == 0) {
36cfb7
@@ -385,7 +395,7 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
36cfb7
 				duparg2("id", *argv);
36cfb7
 			if (get_be64(&id, *argv, 0))
36cfb7
 				invarg("\"id\" value is invalid\n", *argv);
36cfb7
-			rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id);
36cfb7
+			ret = rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id);
36cfb7
 		} else if (strcmp(*argv, "dst") == 0) {
36cfb7
 			inet_prefix addr;
36cfb7
 
36cfb7
@@ -393,8 +403,8 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
36cfb7
 			if (dst_ok++)
36cfb7
 				duparg2("dst", *argv);
36cfb7
 			get_addr(&addr, *argv, AF_INET6);
36cfb7
-			rta_addattr_l(rta, len, LWTUNNEL_IP6_DST,
36cfb7
-				      &addr.data, addr.bytelen);
36cfb7
+			ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_DST,
36cfb7
+					    &addr.data, addr.bytelen);
36cfb7
 		} else if (strcmp(*argv, "tc") == 0) {
36cfb7
 			__u32 tc;
36cfb7
 
36cfb7
@@ -403,7 +413,7 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
36cfb7
 				duparg2("tc", *argv);
36cfb7
 			if (rtnl_dsfield_a2n(&tc, *argv))
36cfb7
 				invarg("\"tc\" value is invalid\n", *argv);
36cfb7
-			rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc);
36cfb7
+			ret = rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc);
36cfb7
 		} else if (strcmp(*argv, "hoplimit") == 0) {
36cfb7
 			__u8 hoplimit;
36cfb7
 
36cfb7
@@ -413,10 +423,13 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
36cfb7
 			if (get_u8(&hoplimit, *argv, 0))
36cfb7
 				invarg("\"hoplimit\" value is invalid\n",
36cfb7
 				       *argv);
36cfb7
-			rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, hoplimit);
36cfb7
+			ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT,
36cfb7
+					   hoplimit);
36cfb7
 		} else {
36cfb7
 			break;
36cfb7
 		}
36cfb7
+		if (ret)
36cfb7
+			break;
36cfb7
 		argc--; argv++;
36cfb7
 	}
36cfb7
 
36cfb7
@@ -427,7 +440,7 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
36cfb7
 	*argcp = argc + 1;
36cfb7
 	*argvp = argv - 1;
36cfb7
 
36cfb7
-	return 0;
36cfb7
+	return ret;
36cfb7
 }
36cfb7
 
36cfb7
 struct lwt_x {
36cfb7
@@ -542,6 +555,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
36cfb7
 	int argc = *argcp;
36cfb7
 	char **argv = *argvp;
36cfb7
 	__u16 type;
36cfb7
+	int ret = 0;
36cfb7
 
36cfb7
 	NEXT_ARG();
36cfb7
 	type = read_encap_type(*argv);
36cfb7
@@ -558,16 +572,16 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
36cfb7
 	nest = rta_nest(rta, 1024, RTA_ENCAP);
36cfb7
 	switch (type) {
36cfb7
 	case LWTUNNEL_ENCAP_MPLS:
36cfb7
-		parse_encap_mpls(rta, len, &argc, &argv);
36cfb7
+		ret = parse_encap_mpls(rta, len, &argc, &argv);
36cfb7
 		break;
36cfb7
 	case LWTUNNEL_ENCAP_IP:
36cfb7
-		parse_encap_ip(rta, len, &argc, &argv);
36cfb7
+		ret = parse_encap_ip(rta, len, &argc, &argv);
36cfb7
 		break;
36cfb7
 	case LWTUNNEL_ENCAP_ILA:
36cfb7
-		parse_encap_ila(rta, len, &argc, &argv);
36cfb7
+		ret = parse_encap_ila(rta, len, &argc, &argv);
36cfb7
 		break;
36cfb7
 	case LWTUNNEL_ENCAP_IP6:
36cfb7
-		parse_encap_ip6(rta, len, &argc, &argv);
36cfb7
+		ret = parse_encap_ip6(rta, len, &argc, &argv);
36cfb7
 		break;
36cfb7
 	case LWTUNNEL_ENCAP_BPF:
36cfb7
 		if (parse_encap_bpf(rta, len, &argc, &argv) < 0)
36cfb7
@@ -577,12 +591,15 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
36cfb7
 		fprintf(stderr, "Error: unsupported encap type\n");
36cfb7
 		break;
36cfb7
 	}
36cfb7
+	if (ret)
36cfb7
+		return ret;
36cfb7
+
36cfb7
 	rta_nest_end(rta, nest);
36cfb7
 
36cfb7
-	rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type);
36cfb7
+	ret = rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type);
36cfb7
 
36cfb7
 	*argcp = argc;
36cfb7
 	*argvp = argv;
36cfb7
 
36cfb7
-	return 0;
36cfb7
+	return ret;
36cfb7
 }
36cfb7
-- 
e138d9
2.21.0
36cfb7