DF-3422 / fix.diff
--- a/sys/netinet/ip_gre.c +++ b/sys/netinet/ip_gre.c @@ -177,6 +177,18 @@ return(0); } + /* + * The GRE header and any optional fields (checksum/offset, key, + * sequence) must actually be present in the packet. hlen is + * computed purely from the attacker-controlled flag bits and can + * exceed the packet length, which would make m_len and + * m_pkthdr.len go negative (DF-3422). + */ + if (m->m_pkthdr.len < hlen) { + m_freem(m); + return (1); + } + m->m_data += hlen; m->m_len -= hlen; m->m_pkthdr.len -= hlen; @@ -189,7 +201,12 @@ } m->m_pkthdr.rcvif = &sc->sc_if; - m->m_flags &= ~M_HASH; + /* + * The outer packet's hash/length-check state does not apply to + * the decapsulated inner packet; force ip_hashfn() to run + * ip_lengthcheck() on it (DF-3422, cf. gif DF-3321). + */ + m->m_flags &= ~(M_HASH | M_LENCHECKED); netisr_queue(isr, m); return(1); /* packet is done, no further processing needed */ } @@ -226,6 +243,18 @@ } else { msiz = MOB_H_SIZ_S; } + + /* + * The mobile header must be present in the packet: msiz is + * selected by the S-bit of a header that a short datagram may + * not carry, and the bcopy() length below would go negative and + * underflow to a huge size_t (DF-3423). + */ + if (m->m_len < (ip->ip_hl << 2) + msiz) { + m_freem(m); + return(IPPROTO_DONE); + } + mip->mi.ip_dst.s_addr = mip->mh.odst; mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8); @@ -244,7 +273,12 @@ * rip_input already decreased ip_len byt the length of the * IP header. */ - ip->ip_len = htons(ntohs(ip->ip_len) + sizeof(struct ip) - msiz); + /* + * NOTE: ip_len is still the FULL outer datagram length + * (DragonFly does not pre-decrement it), so only the mobile + * header size is removed (DF-3423). + */ + ip->ip_len = htons(ntohs(ip->ip_len) - msiz); ip->ip_sum = 0; ip->ip_sum = in_cksum(m, (ip->ip_hl << 2)); @@ -258,6 +292,10 @@ m->m_pkthdr.rcvif = &sc->sc_if; + /* + * The decapsulated packet has not been characterized (DF-3423). + */ + m->m_flags &= ~(M_HASH | M_LENCHECKED); netisr_queue(NETISR_IP, m); return(IPPROTO_DONE); } |