diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index 5b9aa086971..bdcc88438ab 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -164,6 +164,8 @@ icmp_error(n, type, code, dest, destifp) if (m == NULL) goto freeit; icmplen = min(oiplen + 8, oip->ip_len); + if (icmplen < sizeof(struct ip)) + panic("icmp_error: bad length"); m->m_len = icmplen + ICMP_MINLEN; MH_ALIGN(m, m->m_len); icp = mtod(m, struct icmp *); @@ -189,7 +191,7 @@ icmp_error(n, type, code, dest, destifp) } icp->icmp_code = code; - bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen); + m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip); nip = &icp->icmp_ip; /* diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 9d1fe463d34..78753dde135 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -1563,12 +1563,21 @@ ip_forward(m, srcrt) } /* - * Save at most 64 bytes of the packet in case - * we need to generate an ICMP message to the src. + * Save the IP header and at most 8 bytes of the payload, + * in case we need to generate an ICMP message to the src. + * + * We don't use m_copy() because it might return a reference + * to a shared cluster. Both this function and ip_output() + * assume exclusive access to the IP header in `m', so any + * data in a cluster may change before we reach icmp_error(). */ - mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64)); - if (mcopy && (mcopy->m_flags & M_EXT)) - m_copydata(mcopy, 0, sizeof(struct ip), mtod(mcopy, caddr_t)); + MGET(mcopy, M_DONTWAIT, m->m_type); + if (mcopy != NULL) { + M_COPY_PKTHDR(mcopy, m); + mcopy->m_len = imin((IP_VHL_HL(ip->ip_vhl) << 2) + 8, + (int)ip->ip_len); + m_copydata(m, 0, mcopy->m_len, mtod(mcopy, caddr_t)); + } #ifdef IPSTEALTH if (!ipstealth) { @@ -1715,8 +1724,6 @@ ip_forward(m, srcrt) m_freem(mcopy); return; } - if (mcopy->m_flags & M_EXT) - m_copyback(mcopy, 0, sizeof(struct ip), mtod(mcopy, caddr_t)); icmp_error(mcopy, type, code, dest, destifp); }