Fix m_pullup() problem after removing PULLDOWN_TESTs and KAME EXT_*macros.

r354748-354750 replaced the KAME macros with m_pulldown() calls.
Contrary to the rest of the network stack m_len checks before m_pulldown()
were not put in placed (see r354748).
Put these m_len checks in place for now (to go along with the style of the
network stack since the initial commits).  These are not put in for
performance but to avoid an error scenario (even though it also will help
performance at the moment as it avoid allocating an extra mbuf; not because
of the unconditional function call).

The observed error case went like this:
(1) an mbuf with M_EXT arrives and we call m_pullup() unconditionally on it.
(2) m_pullup() will call m_get() unless the requested length is larger than
MHLEN (in which case it'll m_freem() the perfectly fine mbuf) and migrate the
requested length of data and pkthdr into the new mbuf.
(3) If m_get() succeeds, a further m_pullup() call going over MHLEN will fail.
This was observed with failing auto-configuration as an RA packet of
200 bytes exceeded MHLEN and the m_pullup() called from nd6_ra_input()
dropped the mbuf.
(Re-)adding the m_len checks before m_pullup() calls avoids this problems
with mbufs using external storage for now.

MFC after:	3 weeks
Sponsored by:	Netflix
This commit is contained in:
Bjoern A. Zeeb 2019-12-01 00:22:04 +00:00
parent 1b50b999f9
commit a4adf6cc65
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=355254
15 changed files with 220 additions and 151 deletions

View File

@ -566,12 +566,14 @@ carp6_input(struct mbuf **mp, int *offp, int proto)
}
/* verify that we have a complete carp packet */
len = m->m_len;
m = m_pullup(m, *offp + sizeof(*ch));
if (m == NULL) {
CARPSTATS_INC(carps_badlen);
CARP_DEBUG("%s: packet size %u too small\n", __func__, len);
return (IPPROTO_DONE);
if (m->m_len < *offp + sizeof(*ch)) {
len = m->m_len;
m = m_pullup(m, *offp + sizeof(*ch));
if (m == NULL) {
CARPSTATS_INC(carps_badlen);
CARP_DEBUG("%s: packet size %u too small\n", __func__, len);
return (IPPROTO_DONE);
}
}
ch = (struct carp_header *)(mtod(m, caddr_t) + *offp);

View File

@ -517,11 +517,13 @@ tcp6_input(struct mbuf **mp, int *offp, int proto)
struct ip6_hdr *ip6;
m = *mp;
m = m_pullup(m, *offp + sizeof(struct tcphdr));
if (m == NULL) {
*mp = m;
TCPSTAT_INC(tcps_rcvshort);
return (IPPROTO_DONE);
if (m->m_len < *offp + sizeof(struct tcphdr)) {
m = m_pullup(m, *offp + sizeof(struct tcphdr));
if (m == NULL) {
*mp = m;
TCPSTAT_INC(tcps_rcvshort);
return (IPPROTO_DONE);
}
}
/*
@ -708,10 +710,12 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
if (off > sizeof (struct tcphdr)) {
#ifdef INET6
if (isipv6) {
m = m_pullup(m, off0 + off);
if (m == NULL) {
TCPSTAT_INC(tcps_rcvshort);
return (IPPROTO_DONE);
if (m->m_len < off0 + off) {
m = m_pullup(m, off0 + off);
if (m == NULL) {
TCPSTAT_INC(tcps_rcvshort);
return (IPPROTO_DONE);
}
}
ip6 = mtod(m, struct ip6_hdr *);
th = (struct tcphdr *)((caddr_t)ip6 + off0);

View File

@ -73,20 +73,24 @@ dest6_input(struct mbuf **mp, int *offp, int proto)
off = *offp;
/* Validation of the length of the header. */
m = m_pullup(m, off + sizeof(*dstopts));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (IPPROTO_DONE);
if (m->m_len < off + sizeof(*dstopts)) {
m = m_pullup(m, off + sizeof(*dstopts));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (IPPROTO_DONE);
}
}
dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
dstoptlen = (dstopts->ip6d_len + 1) << 3;
m = m_pullup(m, off + dstoptlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (IPPROTO_DONE);
if (m->m_len < off + dstoptlen) {
m = m_pullup(m, off + dstoptlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (IPPROTO_DONE);
}
}
dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
off += dstoptlen;

View File

@ -389,11 +389,13 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
M_ASSERTPKTHDR(m);
m = m_pullup(m, offset + sizeof(struct ip6_frag));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (IPPROTO_DONE);
if (m->m_len < offset + sizeof(struct ip6_frag)) {
m = m_pullup(m, offset + sizeof(struct ip6_frag));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (IPPROTO_DONE);
}
}
ip6 = mtod(m, struct ip6_hdr *);

View File

@ -317,10 +317,12 @@ icmp6_error(struct mbuf *m, int type, int code, int param)
if (off >= 0 && nxt == IPPROTO_ICMPV6) {
struct icmp6_hdr *icp;
m = m_pullup(m, off + sizeof(struct icmp6_hdr));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
if (m->m_len < off + sizeof(struct icmp6_hdr)) {
m = m_pullup(m, off + sizeof(struct icmp6_hdr));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
}
}
oip6 = mtod(m, struct ip6_hdr *);
icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
@ -401,11 +403,13 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
m = *mp;
off = *offp;
m = m_pullup(m, off + sizeof(struct icmp6_hdr));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (IPPROTO_DONE);
if (m->m_len < off + sizeof(struct icmp6_hdr)) {
m = m_pullup(m, off + sizeof(struct icmp6_hdr));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (IPPROTO_DONE);
}
}
/*
@ -566,10 +570,12 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
n->m_pkthdr.len = n0len + (noff - off);
n->m_next = n0;
} else {
n = m_pullup(n, off + sizeof(*nicmp6));
if (n == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
break;
if (n->m_len < off + sizeof(*nicmp6)) {
n = m_pullup(n, off + sizeof(*nicmp6));
if (n == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
break;
}
}
nicmp6 = (struct icmp6_hdr *)(mtod(n, caddr_t) + off);
noff = off;
@ -635,11 +641,14 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
if (pr == NULL)
pr = curthread->td_ucred->cr_prison;
if (mode == FQDN) {
m = m_pullup(m, off + sizeof(struct icmp6_nodeinfo));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (IPPROTO_DONE);
if (m->m_len < off + sizeof(struct icmp6_nodeinfo)) {
m = m_pullup(m, off +
sizeof(struct icmp6_nodeinfo));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (IPPROTO_DONE);
}
}
n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (n)
@ -725,11 +734,13 @@ icmp6_input(struct mbuf **mp, int *offp, int proto)
if (icmp6len < sizeof(struct nd_router_solicit))
goto badlen;
if (send_sendso_input_hook != NULL) {
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (IPPROTO_DONE);
if (m->m_len < off + icmp6len) {
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (IPPROTO_DONE);
}
}
error = send_sendso_input_hook(m, ifp, SND_IN, ip6len);
if (error == 0) {
@ -891,11 +902,13 @@ icmp6_notify_error(struct mbuf **mp, int off, int icmp6len, int code)
goto freeit;
}
m = m_pullup(m, off + sizeof(*icmp6) + sizeof(struct ip6_hdr));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
if (m->m_len < off + sizeof(*icmp6) + sizeof(struct ip6_hdr)) {
m = m_pullup(m, off + sizeof(*icmp6) + sizeof(struct ip6_hdr));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
}
}
icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
eip6 = (struct ip6_hdr *)(icmp6 + 1);
@ -921,11 +934,14 @@ icmp6_notify_error(struct mbuf **mp, int off, int icmp6len, int code)
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
case IPPROTO_AH:
m = m_pullup(m, eoff + sizeof(struct ip6_ext));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
if (m->m_len < eoff + sizeof(struct ip6_ext)) {
m = m_pullup(m, eoff +
sizeof(struct ip6_ext));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
}
}
eh = (struct ip6_ext *)
(mtod(m, caddr_t) + eoff);
@ -944,11 +960,13 @@ icmp6_notify_error(struct mbuf **mp, int off, int icmp6len, int code)
* information that depends on the final
* destination (e.g. path MTU).
*/
m = m_pullup(m, eoff + sizeof(*rth));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
if (m->m_len < eoff + sizeof(*rth)) {
m = m_pullup(m, eoff + sizeof(*rth));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
}
}
rth = (struct ip6_rthdr *)
(mtod(m, caddr_t) + eoff);
@ -965,11 +983,14 @@ icmp6_notify_error(struct mbuf **mp, int off, int icmp6len, int code)
rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
int hops;
m = m_pullup(m, eoff + rthlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
if (m->m_len < eoff + rthlen) {
m = m_pullup(m, eoff + rthlen);
if (m == NULL) {
IP6STAT_INC(
ip6s_exthdrtoolong);
*mp = m;
return (-1);
}
}
rth0 = (struct ip6_rthdr0 *)
(mtod(m, caddr_t) + eoff);
@ -982,11 +1003,14 @@ icmp6_notify_error(struct mbuf **mp, int off, int icmp6len, int code)
nxt = rth->ip6r_nxt;
break;
case IPPROTO_FRAGMENT:
m = m_pullup(m, eoff + sizeof(struct ip6_frag));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
if (m->m_len < eoff + sizeof(struct ip6_frag)) {
m = m_pullup(m, eoff +
sizeof(struct ip6_frag));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = m;
return (-1);
}
}
fh = (struct ip6_frag *)(mtod(m, caddr_t) +
eoff);
@ -1295,11 +1319,14 @@ ni6_input(struct mbuf *m, int off, struct prison *pr)
mtx_unlock(&pr->pr_mtx);
if (!n || n->m_next || n->m_len == 0)
goto bad;
m = m_pullup(m, off + sizeof(struct icmp6_nodeinfo) +
subjlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
goto bad;
if (m->m_len < off + sizeof(struct icmp6_nodeinfo) +
subjlen) {
m = m_pullup(m, off +
sizeof(struct icmp6_nodeinfo) + subjlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
goto bad;
}
}
/* ip6 possibly invalid but not used after. */
ni6 = (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off);
@ -2201,10 +2228,12 @@ icmp6_redirect_input(struct mbuf *m, int off)
ip6 = mtod(m, struct ip6_hdr *);
icmp6len = ntohs(ip6->ip6_plen);
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
if (m->m_len < off + icmp6len) {
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
}
}
ip6 = mtod(m, struct ip6_hdr *);
nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);

View File

@ -969,20 +969,24 @@ ip6_hopopts_input(u_int32_t *plenp, u_int32_t *rtalertp,
struct ip6_hbh *hbh;
/* validation of the length of the header */
m = m_pullup(m, off + sizeof(*hbh));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (-1);
if (m->m_len < off + sizeof(*hbh)) {
m = m_pullup(m, off + sizeof(*hbh));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (-1);
}
}
hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off);
hbhlen = (hbh->ip6h_len + 1) << 3;
m = m_pullup(m, off + hbhlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (-1);
if (m->m_len < off + hbhlen) {
m = m_pullup(m, off + hbhlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (-1);
}
}
hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off);
off += hbhlen;

View File

@ -1745,10 +1745,12 @@ pim6_input(struct mbuf *m, int off, int proto, void *arg __unused)
* Make sure that the IP6 and PIM headers in contiguous memory, and
* possibly the PIM REGISTER header
*/
m = m_pullup(m, off + minlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return (IPPROTO_DONE);
if (m->m_len < off + minlen) {
m = m_pullup(m, off + minlen);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return (IPPROTO_DONE);
}
}
ip6 = mtod(m, struct ip6_hdr *);
pim = (struct pim *)((caddr_t)ip6 + off);

View File

@ -1263,10 +1263,12 @@ mld_input(struct mbuf **mp, int off, int icmp6len)
ifp = m->m_pkthdr.rcvif;
/* Pullup to appropriate size. */
m = m_pullup(m, off + sizeof(*mld));
if (m == NULL) {
ICMP6STAT_INC(icp6s_badlen);
return (IPPROTO_DONE);
if (m->m_len < off + sizeof(*mld)) {
m = m_pullup(m, off + sizeof(*mld));
if (m == NULL) {
ICMP6STAT_INC(icp6s_badlen);
return (IPPROTO_DONE);
}
}
mld = (struct mld_hdr *)(mtod(m, uint8_t *) + off);
if (mld->mld_type == MLD_LISTENER_QUERY &&
@ -1275,10 +1277,12 @@ mld_input(struct mbuf **mp, int off, int icmp6len)
} else {
mldlen = sizeof(struct mld_hdr);
}
m = m_pullup(m, off + mldlen);
if (m == NULL) {
ICMP6STAT_INC(icp6s_badlen);
return (IPPROTO_DONE);
if (m->m_len < off + mldlen) {
m = m_pullup(m, off + mldlen);
if (m == NULL) {
ICMP6STAT_INC(icp6s_badlen);
return (IPPROTO_DONE);
}
}
*mp = m;
ip6 = mtod(m, struct ip6_hdr *);

View File

@ -148,10 +148,12 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len)
goto bads;
}
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
if (m->m_len < off + icmp6len) {
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
}
}
ip6 = mtod(m, struct ip6_hdr *);
nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
@ -652,10 +654,12 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len)
goto bad;
}
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
if (m->m_len < off + icmp6len) {
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
}
}
ip6 = mtod(m, struct ip6_hdr *);
nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off);

View File

@ -190,10 +190,12 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len)
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
goto freeit;
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
if (m->m_len < off + icmp6len) {
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
}
}
ip6 = mtod(m, struct ip6_hdr *);
nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off);
@ -388,10 +390,12 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len)
goto bad;
}
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
if (m->m_len < off + icmp6len) {
m = m_pullup(m, off + icmp6len);
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
return;
}
}
ip6 = mtod(m, struct ip6_hdr *);
nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off);

View File

@ -83,11 +83,13 @@ route6_input(struct mbuf **mp, int *offp, int proto)
}
#endif
m = m_pullup(m, off + sizeof(*rh));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (IPPROTO_DONE);
if (m->m_len < off + sizeof(*rh)) {
m = m_pullup(m, off + sizeof(*rh));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (IPPROTO_DONE);
}
}
ip6 = mtod(m, struct ip6_hdr *);
rh = (struct ip6_rthdr *)((caddr_t)ip6 + off);

View File

@ -103,10 +103,12 @@ sctp6_input_with_port(struct mbuf **i_pak, int *offp, uint16_t port)
SCTP_STAT_INCR_COUNTER64(sctps_inpackets);
/* Get IP, SCTP, and first chunk header together in the first mbuf. */
offset = iphlen + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr);
m = m_pullup(m, offset);
if (m == NULL) {
SCTP_STAT_INCR(sctps_hdrops);
return (IPPROTO_DONE);
if (m->m_len < offset) {
m = m_pullup(m, offset);
if (m == NULL) {
SCTP_STAT_INCR(sctps_hdrops);
return (IPPROTO_DONE);
}
}
ip6 = mtod(m, struct ip6_hdr *);
sh = (struct sctphdr *)(mtod(m, caddr_t) + iphlen);

View File

@ -223,11 +223,13 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
ifp = m->m_pkthdr.rcvif;
m = m_pullup(m, off + sizeof(struct udphdr));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (IPPROTO_DONE);
if (m->m_len < off + sizeof(struct udphdr)) {
m = m_pullup(m, off + sizeof(struct udphdr));
if (m == NULL) {
IP6STAT_INC(ip6s_exthdrtoolong);
*mp = NULL;
return (IPPROTO_DONE);
}
}
ip6 = mtod(m, struct ip6_hdr *);
uh = (struct udphdr *)((caddr_t)ip6 + off);

View File

@ -575,12 +575,14 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/* Figure out header size. */
rplen = HDRSIZE(sav);
m = m_pullup(m, skip + rplen);
if (m == NULL) {
DPRINTF(("ah_input: cannot pullup header\n"));
AHSTAT_INC(ahs_hdrops); /*XXX*/
error = ENOBUFS;
goto bad;
if (m->m_len < skip + rplen) {
m = m_pullup(m, skip + rplen);
if (m == NULL) {
DPRINTF(("ah_input: cannot pullup header\n"));
AHSTAT_INC(ahs_hdrops); /*XXX*/
error = ENOBUFS;
goto bad;
}
}
ah = (struct newah *)(mtod(m, caddr_t) + skip);

View File

@ -308,12 +308,14 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
goto bad;
}
m = m_pullup(m, skip + sizeof(*esp));
if (m == NULL) {
DPRINTF(("%s: cannot pullup header\n", __func__));
ESPSTAT_INC(esps_hdrops); /*XXX*/
error = ENOBUFS;
goto bad;
if (m->m_len < skip + sizeof(*esp)) {
m = m_pullup(m, skip + sizeof(*esp));
if (m == NULL) {
DPRINTF(("%s: cannot pullup header\n", __func__));
ESPSTAT_INC(esps_hdrops); /*XXX*/
error = ENOBUFS;
goto bad;
}
}
esp = (struct newesp *)(mtod(m, caddr_t) + skip);