naccyde / rpms / iproute

Forked from rpms/iproute 7 months ago
Clone

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

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