Module Name:    src
Committed By:   maxv
Date:           Fri Jan 19 13:17:29 UTC 2018

Modified Files:
        src/sys/netinet: ip_icmp.c

Log Message:
Fix a buffer overflow in icmp_error. We create in 'm' a packet that must
contain:

  IPv4 header | Fixed part of ICMP header | Variable part of ICMP header

But we perform length checks on 'totlen', which does not count the IPv4
header.

So now, add sizeof(struct ip) in totlen, and stop doing this m_data
nonsense, just get the pointers as usual.


To generate a diff of this commit:
cvs rdiff -u -r1.162 -r1.163 src/sys/netinet/ip_icmp.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/netinet/ip_icmp.c
diff -u src/sys/netinet/ip_icmp.c:1.162 src/sys/netinet/ip_icmp.c:1.163
--- src/sys/netinet/ip_icmp.c:1.162	Fri Jan 19 12:50:27 2018
+++ src/sys/netinet/ip_icmp.c	Fri Jan 19 13:17:29 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_icmp.c,v 1.162 2018/01/19 12:50:27 maxv Exp $	*/
+/*	$NetBSD: ip_icmp.c,v 1.163 2018/01/19 13:17:29 maxv Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -94,7 +94,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip_icmp.c,v 1.162 2018/01/19 12:50:27 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_icmp.c,v 1.163 2018/01/19 13:17:29 maxv Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ipsec.h"
@@ -260,7 +260,8 @@ icmp_error(struct mbuf *n, int type, int
 	struct icmp *icp;
 	struct mbuf *m;
 	struct m_tag *mtag;
-	unsigned datalen, mblen, totlen;
+	unsigned datalen, mblen;
+	int totlen;
 
 #ifdef ICMPPRINTFS
 	if (icmpprintfs)
@@ -315,10 +316,12 @@ icmp_error(struct mbuf *n, int type, int
 	 * Compute the total length of the new packet. Truncate it if it's
 	 * bigger than the size of a cluster.
 	 */
-	CTASSERT(ICMP_MINLEN <= MCLBYTES);
-	if (datalen + ICMP_MINLEN > MCLBYTES)
-		datalen = MCLBYTES - ICMP_MINLEN;
-	totlen = datalen + ICMP_MINLEN;
+	CTASSERT(ICMP_MINLEN + sizeof(struct ip) <= MCLBYTES);
+	totlen = sizeof(struct ip) + ICMP_MINLEN + datalen;
+	if (totlen > MCLBYTES) {
+		datalen = MCLBYTES - ICMP_MINLEN - sizeof(struct ip);
+		totlen = MCLBYTES;
+	}
 
 	/*
 	 * Allocate the mbuf for the new packet.
@@ -335,26 +338,23 @@ icmp_error(struct mbuf *n, int type, int
 		goto freeit;
 	MCLAIM(m, n->m_owner);
 	m->m_len = totlen;
-
-	/*
-	 * Advance to the ICMP header.
-	 */
-	if ((m->m_flags & M_EXT) == 0) {
-		MH_ALIGN(m, m->m_len);
-	} else {
-		m->m_data += sizeof(struct ip);
-		m->m_len -= sizeof(struct ip);
-	}
+	m->m_pkthdr.len = m->m_len;
+	m_copy_rcvif(m, n);
 
 	if ((u_int)type > ICMP_MAXTYPE)
 		panic("icmp_error");
 	ICMP_STATINC(ICMP_STAT_OUTHIST + type);
 
 	/*
+	 * Get pointers on the IP header and the ICMP header.
+	 */
+	nip = mtod(m, struct ip *);
+	icp = (struct icmp *)(nip + 1);
+
+	/*
 	 * Fill in the fields of the ICMP header: icmp_type, icmp_code
 	 * and icmp_ip. icmp_cksum gets filled later.
 	 */
-	icp = mtod(m, struct icmp *);
 	icp->icmp_type = type;
 	if (type == ICMP_REDIRECT) {
 		icp->icmp_gwaddr.s_addr = dest;
@@ -375,21 +375,9 @@ icmp_error(struct mbuf *n, int type, int
 	m_copydata(n, 0, datalen, (void *)&icp->icmp_ip);
 
 	/*
-	 * Come back to the IP header.
-	 */
-	if ((m->m_flags & M_EXT) == 0 &&
-	    m->m_data - sizeof(struct ip) < m->m_pktdat)
-		panic("icmp len");
-	m->m_data -= sizeof(struct ip);
-	m->m_len += sizeof(struct ip);
-	m->m_pkthdr.len = m->m_len;
-	m_copy_rcvif(m, n);
-
-	/*
 	 * Now, copy the old IP header (without options) in front of the
 	 * ICMP message. The src/dst fields will be swapped in icmp_reflect.
 	 */
-	nip = mtod(m, struct ip *);
 	/* ip_v set in ip_output */
 	nip->ip_hl = sizeof(struct ip) >> 2;
 	nip->ip_tos = 0;

Reply via email to