The data in an mbuf buffer is not necessarily at the start of the allocated buffer. (For instance m_adj() allows data to be trimmed from the start by just advancing the pointer and reducing the length.) This means that the allocated buffer size (m->m_size) and the amount of space from the m_data pointer to the end of the buffer (M_ROOM(m)) are not necessarily the same. Commit864036e251
tried to change the m_inc() function from taking the new allocated-buffer-size to taking the new room-size, but forgot to change the initial "do we already have enough space" check. This meant that if we were trying to extend a buffer which had a leading gap between the buffer start and the data, we might incorrectly decide it didn't need to be extended, and then overrun the end of the buffer, causing memory corruption and an eventual crash. Change the "already big enough?" condition from checking the argument against m->m_size to checking against M_ROOM(). This only makes a difference for the callsite in m_cat(); the other three callsites all start with a freshly allocated mbuf from m_get(), which will have m->m_size == M_ROOM(m). Fixes:864036e251
Fixes: https://bugs.launchpad.net/qemu/+bug/1785670 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org> Message-id: 20180807114501.12370-1-peter.maydell@linaro.org Tested-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
239 lines
5.1 KiB
C
239 lines
5.1 KiB
C
/*
|
|
* Copyright (c) 1995 Danny Gasparovski
|
|
*
|
|
* Please read the file COPYRIGHT for the
|
|
* terms and conditions of the copyright.
|
|
*/
|
|
|
|
/*
|
|
* mbuf's in SLiRP are much simpler than the real mbufs in
|
|
* FreeBSD. They are fixed size, determined by the MTU,
|
|
* so that one whole packet can fit. Mbuf's cannot be
|
|
* chained together. If there's more data than the mbuf
|
|
* could hold, an external g_malloced buffer is pointed to
|
|
* by m_ext (and the data pointers) and M_EXT is set in
|
|
* the flags
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "slirp.h"
|
|
|
|
#define MBUF_THRESH 30
|
|
|
|
/*
|
|
* Find a nice value for msize
|
|
*/
|
|
#define SLIRP_MSIZE\
|
|
(offsetof(struct mbuf, m_dat) + IF_MAXLINKHDR + TCPIPHDR_DELTA + IF_MTU)
|
|
|
|
void
|
|
m_init(Slirp *slirp)
|
|
{
|
|
slirp->m_freelist.qh_link = slirp->m_freelist.qh_rlink = &slirp->m_freelist;
|
|
slirp->m_usedlist.qh_link = slirp->m_usedlist.qh_rlink = &slirp->m_usedlist;
|
|
}
|
|
|
|
void m_cleanup(Slirp *slirp)
|
|
{
|
|
struct mbuf *m, *next;
|
|
|
|
m = (struct mbuf *) slirp->m_usedlist.qh_link;
|
|
while ((struct quehead *) m != &slirp->m_usedlist) {
|
|
next = m->m_next;
|
|
if (m->m_flags & M_EXT) {
|
|
g_free(m->m_ext);
|
|
}
|
|
g_free(m);
|
|
m = next;
|
|
}
|
|
m = (struct mbuf *) slirp->m_freelist.qh_link;
|
|
while ((struct quehead *) m != &slirp->m_freelist) {
|
|
next = m->m_next;
|
|
g_free(m);
|
|
m = next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get an mbuf from the free list, if there are none
|
|
* allocate one
|
|
*
|
|
* Because fragmentation can occur if we alloc new mbufs and
|
|
* free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
|
|
* which tells m_free to actually g_free() it
|
|
*/
|
|
struct mbuf *
|
|
m_get(Slirp *slirp)
|
|
{
|
|
register struct mbuf *m;
|
|
int flags = 0;
|
|
|
|
DEBUG_CALL("m_get");
|
|
|
|
if (slirp->m_freelist.qh_link == &slirp->m_freelist) {
|
|
m = g_malloc(SLIRP_MSIZE);
|
|
slirp->mbuf_alloced++;
|
|
if (slirp->mbuf_alloced > MBUF_THRESH)
|
|
flags = M_DOFREE;
|
|
m->slirp = slirp;
|
|
} else {
|
|
m = (struct mbuf *) slirp->m_freelist.qh_link;
|
|
remque(m);
|
|
}
|
|
|
|
/* Insert it in the used list */
|
|
insque(m,&slirp->m_usedlist);
|
|
m->m_flags = (flags | M_USEDLIST);
|
|
|
|
/* Initialise it */
|
|
m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat);
|
|
m->m_data = m->m_dat;
|
|
m->m_len = 0;
|
|
m->m_nextpkt = NULL;
|
|
m->m_prevpkt = NULL;
|
|
m->resolution_requested = false;
|
|
m->expiration_date = (uint64_t)-1;
|
|
DEBUG_ARG("m = %p", m);
|
|
return m;
|
|
}
|
|
|
|
void
|
|
m_free(struct mbuf *m)
|
|
{
|
|
|
|
DEBUG_CALL("m_free");
|
|
DEBUG_ARG("m = %p", m);
|
|
|
|
if(m) {
|
|
/* Remove from m_usedlist */
|
|
if (m->m_flags & M_USEDLIST)
|
|
remque(m);
|
|
|
|
/* If it's M_EXT, free() it */
|
|
if (m->m_flags & M_EXT) {
|
|
g_free(m->m_ext);
|
|
}
|
|
/*
|
|
* Either free() it or put it on the free list
|
|
*/
|
|
if (m->m_flags & M_DOFREE) {
|
|
m->slirp->mbuf_alloced--;
|
|
g_free(m);
|
|
} else if ((m->m_flags & M_FREELIST) == 0) {
|
|
insque(m,&m->slirp->m_freelist);
|
|
m->m_flags = M_FREELIST; /* Clobber other flags */
|
|
}
|
|
} /* if(m) */
|
|
}
|
|
|
|
/*
|
|
* Copy data from one mbuf to the end of
|
|
* the other.. if result is too big for one mbuf, allocate
|
|
* an M_EXT data segment
|
|
*/
|
|
void
|
|
m_cat(struct mbuf *m, struct mbuf *n)
|
|
{
|
|
/*
|
|
* If there's no room, realloc
|
|
*/
|
|
if (M_FREEROOM(m) < n->m_len)
|
|
m_inc(m, m->m_len + n->m_len);
|
|
|
|
memcpy(m->m_data+m->m_len, n->m_data, n->m_len);
|
|
m->m_len += n->m_len;
|
|
|
|
m_free(n);
|
|
}
|
|
|
|
|
|
/* make m 'size' bytes large from m_data */
|
|
void
|
|
m_inc(struct mbuf *m, int size)
|
|
{
|
|
int datasize;
|
|
|
|
/* some compilers throw up on gotos. This one we can fake. */
|
|
if (M_ROOM(m) > size) {
|
|
return;
|
|
}
|
|
|
|
if (m->m_flags & M_EXT) {
|
|
datasize = m->m_data - m->m_ext;
|
|
m->m_ext = g_realloc(m->m_ext, size + datasize);
|
|
} else {
|
|
datasize = m->m_data - m->m_dat;
|
|
m->m_ext = g_malloc(size + datasize);
|
|
memcpy(m->m_ext, m->m_dat, m->m_size);
|
|
m->m_flags |= M_EXT;
|
|
}
|
|
|
|
m->m_data = m->m_ext + datasize;
|
|
m->m_size = size + datasize;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
m_adj(struct mbuf *m, int len)
|
|
{
|
|
if (m == NULL)
|
|
return;
|
|
if (len >= 0) {
|
|
/* Trim from head */
|
|
m->m_data += len;
|
|
m->m_len -= len;
|
|
} else {
|
|
/* Trim from tail */
|
|
len = -len;
|
|
m->m_len -= len;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Copy len bytes from m, starting off bytes into n
|
|
*/
|
|
int
|
|
m_copy(struct mbuf *n, struct mbuf *m, int off, int len)
|
|
{
|
|
if (len > M_FREEROOM(n))
|
|
return -1;
|
|
|
|
memcpy((n->m_data + n->m_len), (m->m_data + off), len);
|
|
n->m_len += len;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Given a pointer into an mbuf, return the mbuf
|
|
* XXX This is a kludge, I should eliminate the need for it
|
|
* Fortunately, it's not used often
|
|
*/
|
|
struct mbuf *
|
|
dtom(Slirp *slirp, void *dat)
|
|
{
|
|
struct mbuf *m;
|
|
|
|
DEBUG_CALL("dtom");
|
|
DEBUG_ARG("dat = %p", dat);
|
|
|
|
/* bug corrected for M_EXT buffers */
|
|
for (m = (struct mbuf *) slirp->m_usedlist.qh_link;
|
|
(struct quehead *) m != &slirp->m_usedlist;
|
|
m = m->m_next) {
|
|
if (m->m_flags & M_EXT) {
|
|
if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) )
|
|
return m;
|
|
} else {
|
|
if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) )
|
|
return m;
|
|
}
|
|
}
|
|
|
|
DEBUG_ERROR((dfd, "dtom failed"));
|
|
|
|
return (struct mbuf *)0;
|
|
}
|