Avoid using m_clget() on a mbuf already in use, especially when we

need the data in the mbuf later and m_clget() changes some fields
overlaid to regular mbuf data. Instead, rearange code a bit, create
data into a new allocated buffer and and use MEXTADD to attach it to
the mbuf, if the mbuf internal space is not sufficient.

This fixes a crash on sparc64 (and probably all other archs where
sizeof(int) != sizeof(struct file *)) when running
regress/sys/kern/unfdpass.

Idea for solution from Matt Thomas, with additional input from YAMAMOTO
Takashi.
This commit is contained in:
martin 2003-12-29 22:08:02 +00:00
parent 3dcb42ad4b
commit 44b17951f2

View File

@ -1,4 +1,4 @@
/* $NetBSD: uipc_usrreq.c,v 1.72 2003/11/29 10:02:42 matt Exp $ */
/* $NetBSD: uipc_usrreq.c,v 1.73 2003/12/29 22:08:02 martin Exp $ */
/*-
* Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
@ -103,7 +103,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uipc_usrreq.c,v 1.72 2003/11/29 10:02:42 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: uipc_usrreq.c,v 1.73 2003/12/29 22:08:02 martin Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -974,8 +974,8 @@ unp_internalize(control, p)
struct proc *p;
{
struct filedesc *fdescp = p->p_fd;
struct cmsghdr *cm = mtod(control, struct cmsghdr *);
struct file **rp;
struct cmsghdr *newcm, *cm = mtod(control, struct cmsghdr *);
struct file **rp, **files;
struct file *fp;
int i, fd, *fdp;
int nfds;
@ -997,37 +997,31 @@ unp_internalize(control, p)
}
/* Make sure we have room for the struct file pointers */
morespace:
neededspace = CMSG_SPACE(nfds * sizeof(struct file *)) -
control->m_len;
if (neededspace > M_TRAILINGSPACE(control)) {
/* if we already have a cluster, the message is just too big */
if (control->m_flags & M_EXT)
/* allocate new space and copy header into it */
newcm = malloc(
CMSG_SPACE(nfds * sizeof(struct file *)),
M_MBUF, M_WAITOK);
if (newcm == NULL)
return (E2BIG);
/* allocate a cluster and try again */
m_clget(control, M_WAIT);
if ((control->m_flags & M_EXT) == 0)
return (ENOBUFS); /* allocation failed */
/* copy the data to the cluster */
memcpy(mtod(control, char *), cm, cm->cmsg_len);
cm = mtod(control, struct cmsghdr *);
goto morespace;
memcpy(newcm, cm, sizeof(struct cmsghdr));
files = (struct file **)CMSG_DATA(newcm);
} else {
/* we can convert in-place */
newcm = NULL;
files = (struct file **)CMSG_DATA(cm);
}
/* adjust message & mbuf to note amount of space actually used. */
cm->cmsg_len = CMSG_LEN(nfds * sizeof(struct file *));
control->m_len = CMSG_SPACE(nfds * sizeof(struct file *));
/*
* Transform the file descriptors into struct file pointers, in
* reverse order so that if pointers are bigger than ints, the
* int won't get until we're done.
*/
fdp = ((int *)CMSG_DATA(cm)) + nfds - 1;
rp = ((struct file **)CMSG_DATA(cm)) + nfds - 1;
fdp = (int *)CMSG_DATA(cm) + nfds - 1;
rp = files + nfds - 1;
for (i = 0; i < nfds; i++) {
fp = fdescp->fd_ofiles[*fdp--];
simple_lock(&fp->f_slock);
@ -1041,6 +1035,20 @@ unp_internalize(control, p)
simple_unlock(&fp->f_slock);
unp_rights++;
}
if (newcm) {
if (control->m_flags & M_EXT)
MEXTREMOVE(control);
MEXTADD(control, newcm,
CMSG_SPACE(nfds * sizeof(struct file *)),
M_MBUF, NULL, NULL);
cm = newcm;
}
/* adjust message & mbuf to note amount of space actually used. */
cm->cmsg_len = CMSG_LEN(nfds * sizeof(struct file *));
control->m_len = CMSG_SPACE(nfds * sizeof(struct file *));
return (0);
}