naccyde / rpms / iproute

Forked from rpms/iproute 9 months ago
Clone

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

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