From owner-svn-src-head@freebsd.org  Fri Sep 29 06:24:46 2017
Return-Path: <owner-svn-src-head@freebsd.org>
Delivered-To: svn-src-head@mailman.ysv.freebsd.org
Received: from mx1.freebsd.org (mx1.freebsd.org
 [IPv6:2001:1900:2254:206a::19:1])
 by mailman.ysv.freebsd.org (Postfix) with ESMTP id B4BA6E2497F;
 Fri, 29 Sep 2017 06:24:46 +0000 (UTC) (envelope-from ae@FreeBSD.org)
Received: from repo.freebsd.org (repo.freebsd.org
 [IPv6:2610:1c1:1:6068::e6a:0])
 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
 (Client did not present a certificate)
 by mx1.freebsd.org (Postfix) with ESMTPS id 85E327FB55;
 Fri, 29 Sep 2017 06:24:46 +0000 (UTC) (envelope-from ae@FreeBSD.org)
Received: from repo.freebsd.org ([127.0.1.37])
 by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v8T6OjXo085431;
 Fri, 29 Sep 2017 06:24:45 GMT (envelope-from ae@FreeBSD.org)
Received: (from ae@localhost)
 by repo.freebsd.org (8.15.2/8.15.2/Submit) id v8T6Ojhq085430;
 Fri, 29 Sep 2017 06:24:45 GMT (envelope-from ae@FreeBSD.org)
Message-Id: <201709290624.v8T6Ojhq085430@repo.freebsd.org>
X-Authentication-Warning: repo.freebsd.org: ae set sender to ae@FreeBSD.org
 using -f
From: "Andrey V. Elsukov" <ae@FreeBSD.org>
Date: Fri, 29 Sep 2017 06:24:45 +0000 (UTC)
To: src-committers@freebsd.org, svn-src-all@freebsd.org,
 svn-src-head@freebsd.org
Subject: svn commit: r324098 - head/sys/netinet
X-SVN-Group: head
X-SVN-Commit-Author: ae
X-SVN-Commit-Paths: head/sys/netinet
X-SVN-Commit-Revision: 324098
X-SVN-Commit-Repository: base
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-BeenThere: svn-src-head@freebsd.org
X-Mailman-Version: 2.1.23
Precedence: list
List-Id: SVN commit messages for the src tree for head/-current
 <svn-src-head.freebsd.org>
List-Unsubscribe: <https://lists.freebsd.org/mailman/options/svn-src-head>,
 <mailto:svn-src-head-request@freebsd.org?subject=unsubscribe>
List-Archive: <http://lists.freebsd.org/pipermail/svn-src-head/>
List-Post: <mailto:svn-src-head@freebsd.org>
List-Help: <mailto:svn-src-head-request@freebsd.org?subject=help>
List-Subscribe: <https://lists.freebsd.org/mailman/listinfo/svn-src-head>,
 <mailto:svn-src-head-request@freebsd.org?subject=subscribe>
X-List-Received-Date: Fri, 29 Sep 2017 06:24:46 -0000

Author: ae
Date: Fri Sep 29 06:24:45 2017
New Revision: 324098
URL: https://svnweb.freebsd.org/changeset/base/324098

Log:
  Some mbuf related fixes in icmp_error()
  
  * check mbuf length before doing mtod() and accessing to IP header;
  * update oip pointer and all depending pointers after m_pullup();
  * remove extra checks and extra parentheses, wrap long lines;
  
  PR:		222670
  Reported by:	Prabhakar Lakhera
  MFC after:	1 week

Modified:
  head/sys/netinet/ip_icmp.c

Modified: head/sys/netinet/ip_icmp.c
==============================================================================
--- head/sys/netinet/ip_icmp.c	Fri Sep 29 04:52:15 2017	(r324097)
+++ head/sys/netinet/ip_icmp.c	Fri Sep 29 06:24:45 2017	(r324098)
@@ -185,17 +185,14 @@ kmod_icmpstat_inc(int statnum)
 void
 icmp_error(struct mbuf *n, int type, int code, uint32_t dest, int mtu)
 {
-	struct ip *oip = mtod(n, struct ip *), *nip;
-	unsigned oiphlen = oip->ip_hl << 2;
+	struct ip *oip, *nip;
 	struct icmp *icp;
 	struct mbuf *m;
-	unsigned icmplen, icmpelen, nlen;
+	unsigned icmplen, icmpelen, nlen, oiphlen;
 
-	KASSERT((u_int)type <= ICMP_MAXTYPE, ("%s: illegal ICMP type", __func__));
-#ifdef ICMPPRINTFS
-	if (icmpprintfs)
-		printf("icmp_error(%p, %x, %d)\n", oip, type, code);
-#endif
+	KASSERT((u_int)type <= ICMP_MAXTYPE, ("%s: illegal ICMP type",
+	    __func__));
+
 	if (type != ICMP_REDIRECT)
 		ICMPSTAT_INC(icps_error);
 	/*
@@ -207,19 +204,28 @@ icmp_error(struct mbuf *n, int type, int code, uint32_
 	 */
 	if (n->m_flags & M_DECRYPTED)
 		goto freeit;
-	if (oip->ip_off & htons(~(IP_MF|IP_DF)))
-		goto freeit;
 	if (n->m_flags & (M_BCAST|M_MCAST))
 		goto freeit;
+
+	/* Drop if IP header plus 8 bytes is not contiguous in first mbuf. */
+	if (n->m_len < sizeof(struct ip) + ICMP_MINLEN)
+		goto freeit;
+	oip = mtod(n, struct ip *);
+	oiphlen = oip->ip_hl << 2;
+	if (n->m_len < oiphlen + ICMP_MINLEN)
+		goto freeit;
+#ifdef ICMPPRINTFS
+	if (icmpprintfs)
+		printf("icmp_error(%p, %x, %d)\n", oip, type, code);
+#endif
+	if (oip->ip_off & htons(~(IP_MF|IP_DF)))
+		goto freeit;
 	if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
-	  n->m_len >= oiphlen + ICMP_MINLEN &&
-	  !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiphlen))->icmp_type)) {
+	    !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip +
+		oiphlen))->icmp_type)) {
 		ICMPSTAT_INC(icps_oldicmp);
 		goto freeit;
 	}
-	/* Drop if IP header plus 8 bytes is not contignous in first mbuf. */
-	if (oiphlen + 8 > n->m_len)
-		goto freeit;
 	/*
 	 * Calculate length to quote from original packet and
 	 * prevent the ICMP mbuf from overflowing.
@@ -235,9 +241,10 @@ icmp_error(struct mbuf *n, int type, int code, uint32_
 		    n->m_next == NULL)
 			goto stdreply;
 		if (n->m_len < oiphlen + sizeof(struct tcphdr) &&
-		    ((n = m_pullup(n, oiphlen + sizeof(struct tcphdr))) == NULL))
+		    (n = m_pullup(n, oiphlen + sizeof(struct tcphdr))) == NULL)
 			goto freeit;
-		th = (struct tcphdr *)((caddr_t)oip + oiphlen);
+		oip = mtod(n, struct ip *);
+		th = mtodo(n, oiphlen);
 		tcphlen = th->th_off << 2;
 		if (tcphlen < sizeof(struct tcphdr))
 			goto freeit;
@@ -245,8 +252,8 @@ icmp_error(struct mbuf *n, int type, int code, uint32_
 			goto freeit;
 		if (oiphlen + tcphlen > n->m_len && n->m_next == NULL)
 			goto stdreply;
-		if (n->m_len < oiphlen + tcphlen && 
-		    ((n = m_pullup(n, oiphlen + tcphlen)) == NULL))
+		if (n->m_len < oiphlen + tcphlen &&
+		    (n = m_pullup(n, oiphlen + tcphlen)) == NULL)
 			goto freeit;
 		icmpelen = max(tcphlen, min(V_icmp_quotelen,
 		    ntohs(oip->ip_len) - oiphlen));
@@ -262,24 +269,31 @@ icmp_error(struct mbuf *n, int type, int code, uint32_
 		if (n->m_len < oiphlen + sizeof(struct sctphdr) &&
 		    (n = m_pullup(n, oiphlen + sizeof(struct sctphdr))) == NULL)
 			goto freeit;
+		oip = mtod(n, struct ip *);
 		icmpelen = max(sizeof(struct sctphdr),
 		    min(V_icmp_quotelen, ntohs(oip->ip_len) - oiphlen));
-		sh = (struct sctphdr *)((caddr_t)oip + oiphlen);
+		sh = mtodo(n, oiphlen);
 		if (ntohl(sh->v_tag) == 0 &&
-		    ntohs(oip->ip_len) >= oiphlen + sizeof(struct sctphdr) + 8 &&
+		    ntohs(oip->ip_len) >= oiphlen +
+		    sizeof(struct sctphdr) + 8 &&
 		    (n->m_len >= oiphlen + sizeof(struct sctphdr) + 8 ||
 		     n->m_next != NULL)) {
 			if (n->m_len < oiphlen + sizeof(struct sctphdr) + 8 &&
-			    (n = m_pullup(n, oiphlen + sizeof(struct sctphdr) + 8)) == NULL)
+			    (n = m_pullup(n, oiphlen +
+			    sizeof(struct sctphdr) + 8)) == NULL)
 				goto freeit;
+			oip = mtod(n, struct ip *);
+			sh = mtodo(n, oiphlen);
 			ch = (struct sctp_chunkhdr *)(sh + 1);
 			if (ch->chunk_type == SCTP_INITIATION) {
 				icmpelen = max(sizeof(struct sctphdr) + 8,
-				    min(V_icmp_quotelen, ntohs(oip->ip_len) - oiphlen));
+				    min(V_icmp_quotelen, ntohs(oip->ip_len) -
+				    oiphlen));
 			}
 		}
 	} else
-stdreply:	icmpelen = max(8, min(V_icmp_quotelen, ntohs(oip->ip_len) - oiphlen));
+stdreply:	icmpelen = max(8, min(V_icmp_quotelen, ntohs(oip->ip_len) -
+		    oiphlen));
 
 	icmplen = min(oiphlen + icmpelen, nlen);
 	if (icmplen < sizeof(struct ip))
@@ -294,7 +308,8 @@ stdreply:	icmpelen = max(8, min(V_icmp_quotelen, ntohs
 #ifdef MAC
 	mac_netinet_icmp_reply(n, m);
 #endif
-	icmplen = min(icmplen, M_TRAILINGSPACE(m) - sizeof(struct ip) - ICMP_MINLEN);
+	icmplen = min(icmplen, M_TRAILINGSPACE(m) -
+	    sizeof(struct ip) - ICMP_MINLEN);
 	m_align(m, ICMP_MINLEN + icmplen);
 	m->m_len = ICMP_MINLEN + icmplen;