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;