From owner-dev-commits-src-main@freebsd.org Fri Jul 16 10:08:48 2021 Return-Path: Delivered-To: dev-commits-src-main@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 534B565EFEE; Fri, 16 Jul 2021 10:08:48 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4GR6QS1kMRz4Y5W; Fri, 16 Jul 2021 10:08:48 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 243791B3D9; Fri, 16 Jul 2021 10:08:48 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 16GA8mmL071539; Fri, 16 Jul 2021 10:08:48 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 16GA8mrw071538; Fri, 16 Jul 2021 10:08:48 GMT (envelope-from git) Date: Fri, 16 Jul 2021 10:08:48 GMT Message-Id: <202107161008.16GA8mrw071538@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Randall Stewart Subject: git: 1d171e5ab962 - main - tcp: Lro needs to validate that it does not go beyond the end of the mbuf as it parses. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: rrs X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 1d171e5ab9622e512ea4fc95b8eb3682d1858c6f Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-main@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commit messages for the main branch of the src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 16 Jul 2021 10:08:48 -0000 The branch main has been updated by rrs: URL: https://cgit.FreeBSD.org/src/commit/?id=1d171e5ab9622e512ea4fc95b8eb3682d1858c6f commit 1d171e5ab9622e512ea4fc95b8eb3682d1858c6f Author: Randall Stewart AuthorDate: 2021-07-16 10:07:13 +0000 Commit: Randall Stewart CommitDate: 2021-07-16 10:07:13 +0000 tcp: Lro needs to validate that it does not go beyond the end of the mbuf as it parses. Currently the LRO parser, if given a packet that say has ETH+IP header but the TCP header is in the next mbuf (split), would walk garbage. Lets make sure we keep track as we parse of the length and return NULL anytime we exceed the length of the mbuf. Reviewed by: tuexen, hselasky Sponsored by: Netflix Inc. Differential Revision: https://reviews.freebsd.org/D31195 --- sys/netinet/tcp_lro.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/sys/netinet/tcp_lro.c b/sys/netinet/tcp_lro.c index 23e64b29b296..f0a996f685c3 100644 --- a/sys/netinet/tcp_lro.c +++ b/sys/netinet/tcp_lro.c @@ -228,7 +228,7 @@ struct vxlan_header { }; static inline void * -tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, bool is_vxlan) +tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, bool is_vxlan, int mlen) { const struct ether_vlan_header *eh; void *old; @@ -258,16 +258,21 @@ tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, } /* advance to next header */ ptr = (uint8_t *)ptr + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + mlen -= (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN); } else { eth_type = eh->evl_encap_proto; /* advance to next header */ + mlen -= ETHER_HDR_LEN; ptr = (uint8_t *)ptr + ETHER_HDR_LEN; } - + if (__predict_false(mlen <= 0)) + return (NULL); switch (eth_type) { #ifdef INET case htons(ETHERTYPE_IP): parser->ip4 = ptr; + if (__predict_false(mlen < sizeof(struct ip))) + return (NULL); /* Ensure there are no IPv4 options. */ if ((parser->ip4->ip_hl << 2) != sizeof (*parser->ip4)) break; @@ -275,12 +280,15 @@ tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, if (parser->ip4->ip_off & htons(IP_MF|IP_OFFMASK)) break; ptr = (uint8_t *)ptr + (parser->ip4->ip_hl << 2); + mlen -= sizeof(struct ip); if (update_data) { parser->data.s_addr.v4 = parser->ip4->ip_src; parser->data.d_addr.v4 = parser->ip4->ip_dst; } switch (parser->ip4->ip_p) { case IPPROTO_UDP: + if (__predict_false(mlen < sizeof(struct udphdr))) + return (NULL); parser->udp = ptr; if (update_data) { parser->data.lro_type = LRO_TYPE_IPV4_UDP; @@ -294,6 +302,8 @@ tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, return (ptr); case IPPROTO_TCP: parser->tcp = ptr; + if (__predict_false(mlen < sizeof(struct tcphdr))) + return (NULL); if (update_data) { parser->data.lro_type = LRO_TYPE_IPV4_TCP; parser->data.s_port = parser->tcp->th_sport; @@ -301,6 +311,8 @@ tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, } else { MPASS(parser->data.lro_type == LRO_TYPE_IPV4_TCP); } + if (__predict_false(mlen < (parser->tcp->th_off << 2))) + return (NULL); ptr = (uint8_t *)ptr + (parser->tcp->th_off << 2); parser->total_hdr_len = (uint8_t *)ptr - (uint8_t *)old; return (ptr); @@ -312,13 +324,18 @@ tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, #ifdef INET6 case htons(ETHERTYPE_IPV6): parser->ip6 = ptr; + if (__predict_false(mlen < sizeof(struct ip6_hdr))) + return (NULL); ptr = (uint8_t *)ptr + sizeof(*parser->ip6); if (update_data) { parser->data.s_addr.v6 = parser->ip6->ip6_src; parser->data.d_addr.v6 = parser->ip6->ip6_dst; } + mlen -= sizeof(struct ip6_hdr); switch (parser->ip6->ip6_nxt) { case IPPROTO_UDP: + if (__predict_false(mlen < sizeof(struct udphdr))) + return (NULL); parser->udp = ptr; if (update_data) { parser->data.lro_type = LRO_TYPE_IPV6_UDP; @@ -331,6 +348,8 @@ tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, parser->total_hdr_len = (uint8_t *)ptr - (uint8_t *)old; return (ptr); case IPPROTO_TCP: + if (__predict_false(mlen < sizeof(struct tcphdr))) + return (NULL); parser->tcp = ptr; if (update_data) { parser->data.lro_type = LRO_TYPE_IPV6_TCP; @@ -339,6 +358,8 @@ tcp_lro_low_level_parser(void *ptr, struct lro_parser *parser, bool update_data, } else { MPASS(parser->data.lro_type == LRO_TYPE_IPV6_TCP); } + if (__predict_false(mlen < (parser->tcp->th_off << 2))) + return (NULL); ptr = (uint8_t *)ptr + (parser->tcp->th_off << 2); parser->total_hdr_len = (uint8_t *)ptr - (uint8_t *)old; return (ptr); @@ -363,7 +384,7 @@ tcp_lro_parser(struct mbuf *m, struct lro_parser *po, struct lro_parser *pi, boo void *data_ptr; /* Try to parse outer headers first. */ - data_ptr = tcp_lro_low_level_parser(m->m_data, po, update_data, false); + data_ptr = tcp_lro_low_level_parser(m->m_data, po, update_data, false, m->m_len); if (data_ptr == NULL || po->total_hdr_len > m->m_len) return (NULL); @@ -383,8 +404,9 @@ tcp_lro_parser(struct mbuf *m, struct lro_parser *po, struct lro_parser *pi, boo break; /* Try to parse inner headers. */ - data_ptr = tcp_lro_low_level_parser(data_ptr, pi, update_data, true); - if (data_ptr == NULL || pi->total_hdr_len > m->m_len) + data_ptr = tcp_lro_low_level_parser(data_ptr, pi, update_data, true, + (m->m_len - ((caddr_t)data_ptr - m->m_data))); + if (data_ptr == NULL || (pi->total_hdr_len + po->total_hdr_len) > m->m_len) break; /* Verify supported header types. */