summaryrefslogtreecommitdiffstats
path: root/sys/kern/uipc_usrreq.c
diff options
context:
space:
mode:
authordwmalone <dwmalone@FreeBSD.org>2001-10-04 13:11:48 +0000
committerdwmalone <dwmalone@FreeBSD.org>2001-10-04 13:11:48 +0000
commit86cf053ae0113e103de743ed432880fb2b462149 (patch)
tree6615cc44c88feefec804ce8ebb7cd6284b4483b6 /sys/kern/uipc_usrreq.c
parenta41aa2f6cfba0d70cf320783974b86c4bb644eda (diff)
downloadFreeBSD-src-86cf053ae0113e103de743ed432880fb2b462149.zip
FreeBSD-src-86cf053ae0113e103de743ed432880fb2b462149.tar.gz
Hopefully improve control message passing over Unix domain sockets.
1) Allow the sending of more than one control message at a time over a unix domain socket. This should cover the PR 29499. 2) This requires that unp_{ex,in}ternalize and unp_scan understand mbufs with more than one control message at a time. 3) Internalize and externalize used to work on the mbuf in-place. This made life quite complicated and the code for sizeof(int) < sizeof(file *) could end up doing the wrong thing. The patch always create a new mbuf/cluster now. This resulted in the change of the prototype for the domain externalise function. 4) You can now send SCM_TIMESTAMP messages. 5) Always use CMSG_DATA(cm) to determine the start where the data in unp_{ex,in}ternalize. It was using ((struct cmsghdr *)cm + 1) in some places, which gives the wrong alignment on the alpha. (NetBSD made this fix some time ago). This results in an ABI change for discriptor passing and creds passing on the alpha. (Probably on the IA64 and Spare ports too). 6) Fix userland programs to use CMSG_* macros too. 7) Be more careful about freeing mbufs containing (file *)s. This is made possible by the prototype change of externalise. PR: 29499 MFC after: 6 weeks
Diffstat (limited to 'sys/kern/uipc_usrreq.c')
-rw-r--r--sys/kern/uipc_usrreq.c437
1 files changed, 265 insertions, 172 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 52c60cf..7480773 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -90,7 +90,8 @@ static void unp_gc __P((void));
static void unp_scan __P((struct mbuf *, void (*)(struct file *)));
static void unp_mark __P((struct file *));
static void unp_discard __P((struct file *));
-static int unp_internalize __P((struct mbuf *, struct thread *));
+static void unp_freerights __P((struct file **, int));
+static int unp_internalize __P((struct mbuf **, struct thread *));
static int unp_listen __P((struct unpcb *, struct proc *));
static int
@@ -274,7 +275,7 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
goto release;
}
- if (control && (error = unp_internalize(control, td)))
+ if (control && (error = unp_internalize(&control, td)))
goto release;
switch (so->so_type) {
@@ -952,80 +953,127 @@ unp_drain()
}
#endif
+static void
+unp_freerights(rp, fdcount)
+ struct file **rp;
+ int fdcount;
+{
+ int i;
+ struct file *fp;
+
+ for (i = 0; i < fdcount; i++) {
+ fp = *rp;
+ /*
+ * zero the pointer before calling
+ * unp_discard since it may end up
+ * in unp_gc()..
+ */
+ *rp++ = 0;
+ unp_discard(fp);
+ }
+}
+
int
-unp_externalize(rights)
- struct mbuf *rights;
+unp_externalize(control, controlp)
+ struct mbuf *control, **controlp;
{
struct thread *td = curthread; /* XXX */
- register int i;
- register struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
- register int *fdp;
- register struct file **rp;
- register struct file *fp;
- int newfds = (cm->cmsg_len - (CMSG_DATA(cm) - (u_char *)cm))
- / sizeof (struct file *);
+ struct cmsghdr *cm = mtod(control, struct cmsghdr *);
+ int i;
+ int *fdp;
+ struct file **rp;
+ struct file *fp;
+ void *data;
+ socklen_t clen = control->m_len, datalen;
+ int error, newfds;
int f;
+ u_int newlen;
- /*
- * if the new FD's will not fit, then we free them all
- */
- if (!fdavail(td, newfds)) {
- rp = (struct file **)CMSG_DATA(cm);
- for (i = 0; i < newfds; i++) {
- fp = *rp;
+ error = 0;
+ if (controlp != NULL) /* controlp == NULL => free control messages */
+ *controlp = NULL;
+
+ while (cm != NULL) {
+ if (sizeof(*cm) > clen || cm->cmsg_len > clen) {
+ error = EINVAL;
+ break;
+ }
+
+ data = CMSG_DATA(cm);
+ datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
+
+ if (cm->cmsg_level == SOL_SOCKET
+ && cm->cmsg_type == SCM_RIGHTS) {
+ newfds = datalen / sizeof(struct file *);
+ rp = data;
+
+ /* If we're not outputting the discriptors free them. */
+ if (error || controlp == NULL) {
+ unp_freerights(rp, newfds);
+ goto next;
+ }
+ /* if the new FD's will not fit free them. */
+ if (!fdavail(td, newfds)) {
+ error = EMSGSIZE;
+ unp_freerights(rp, newfds);
+ goto next;
+ }
/*
- * zero the pointer before calling unp_discard,
- * since it may end up in unp_gc()..
+ * now change each pointer to an fd in the global
+ * table to an integer that is the index to the
+ * local fd table entry that we set up to point
+ * to the global one we are transferring.
*/
- *rp++ = 0;
- unp_discard(fp);
- }
- return (EMSGSIZE);
- }
- /*
- * now change each pointer to an fd in the global table to
- * an integer that is the index to the local fd table entry
- * that we set up to point to the global one we are transferring.
- * If sizeof (struct file *) is bigger than or equal to sizeof int,
- * then do it in forward order. In that case, an integer will
- * always come in the same place or before its corresponding
- * struct file pointer.
- * If sizeof (struct file *) is smaller than sizeof int, then
- * do it in reverse order.
- */
- if (sizeof (struct file *) >= sizeof (int)) {
- fdp = (int *)(cm + 1);
- rp = (struct file **)CMSG_DATA(cm);
- for (i = 0; i < newfds; i++) {
- if (fdalloc(td, 0, &f))
- panic("unp_externalize");
- fp = *rp++;
- td->td_proc->p_fd->fd_ofiles[f] = fp;
- fp->f_msgcount--;
- unp_rights--;
- *fdp++ = f;
+ newlen = newfds * sizeof(int);
+ *controlp = sbcreatecontrol(NULL, newlen,
+ SCM_RIGHTS, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = E2BIG;
+ unp_freerights(rp, newfds);
+ goto next;
+ }
+
+ fdp = (int *)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ for (i = 0; i < newfds; i++) {
+ if (fdalloc(td, 0, &f))
+ panic("unp_externalize fdalloc failed");
+ fp = *rp++;
+ td->td_proc->p_fd->fd_ofiles[f] = fp;
+ fp->f_msgcount--;
+ unp_rights--;
+ *fdp++ = f;
+ }
+ } else { /* We can just copy anything else across */
+ if (error || controlp == NULL)
+ goto next;
+ *controlp = sbcreatecontrol(NULL, datalen,
+ cm->cmsg_type, cm->cmsg_level);
+ if (*controlp == NULL) {
+ error = ENOBUFS;
+ goto next;
+ }
+ bcopy(data,
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *)),
+ datalen);
}
- } else {
- fdp = (int *)(cm + 1) + newfds - 1;
- rp = (struct file **)CMSG_DATA(cm) + newfds - 1;
- for (i = 0; i < newfds; i++) {
- if (fdalloc(td, 0, &f))
- panic("unp_externalize");
- fp = *rp--;
- td->td_proc->p_fd->fd_ofiles[f] = fp;
- fp->f_msgcount--;
- unp_rights--;
- *fdp-- = f;
+
+ controlp = &(*controlp)->m_next;
+
+next:
+ if (CMSG_SPACE(datalen) < clen) {
+ clen -= CMSG_SPACE(datalen);
+ cm = (struct cmsghdr *)
+ ((caddr_t)cm + CMSG_SPACE(datalen));
+ } else {
+ clen = 0;
+ cm = NULL;
}
}
- /*
- * Adjust length, in case sizeof(struct file *) and sizeof(int)
- * differs.
- */
- cm->cmsg_len = CMSG_LEN(newfds * sizeof(int));
- rights->m_len = cm->cmsg_len;
- return (0);
+ m_freem(control);
+
+ return (error);
}
void
@@ -1043,109 +1091,134 @@ unp_init(void)
#endif
static int
-unp_internalize(control, td)
- struct mbuf *control;
+unp_internalize(controlp, td)
+ struct mbuf **controlp;
struct thread *td;
{
+ struct mbuf *control = *controlp;
struct proc *p = td->td_proc;
struct filedesc *fdescp = p->p_fd;
- register struct cmsghdr *cm = mtod(control, struct cmsghdr *);
- register struct file **rp;
- register struct file *fp;
- register int i, fd, *fdp;
- register struct cmsgcred *cmcred;
- int oldfds;
+ struct cmsghdr *cm = mtod(control, struct cmsghdr *);
+ struct cmsgcred *cmcred;
+ struct file **rp;
+ struct file *fp;
+ struct timeval *tv;
+ int i, fd, *fdp;
+ void *data;
+ socklen_t clen = control->m_len, datalen;
+ int error, oldfds;
u_int newlen;
- if ((cm->cmsg_type != SCM_RIGHTS && cm->cmsg_type != SCM_CREDS) ||
- cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len)
- return (EINVAL);
+ error = 0;
+ *controlp = NULL;
- /*
- * Fill in credential information.
- */
- if (cm->cmsg_type == SCM_CREDS) {
- cmcred = (struct cmsgcred *)(cm + 1);
- cmcred->cmcred_pid = p->p_pid;
- cmcred->cmcred_uid = p->p_ucred->cr_ruid;
- cmcred->cmcred_gid = p->p_ucred->cr_rgid;
- cmcred->cmcred_euid = p->p_ucred->cr_uid;
- cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
+ while (cm != NULL) {
+ if (sizeof(*cm) > clen || cm->cmsg_level != SOL_SOCKET
+ || cm->cmsg_len > clen) {
+ error = EINVAL;
+ goto out;
+ }
+
+ data = CMSG_DATA(cm);
+ datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
+
+ switch (cm->cmsg_type) {
+ /*
+ * Fill in credential information.
+ */
+ case SCM_CREDS:
+ *controlp = sbcreatecontrol(NULL, sizeof(*cmcred),
+ SCM_CREDS, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+
+ cmcred = (struct cmsgcred *)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ cmcred->cmcred_pid = p->p_pid;
+ cmcred->cmcred_uid = p->p_ucred->cr_ruid;
+ cmcred->cmcred_gid = p->p_ucred->cr_rgid;
+ cmcred->cmcred_euid = p->p_ucred->cr_uid;
+ cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
CMGROUP_MAX);
- for (i = 0; i < cmcred->cmcred_ngroups; i++)
- cmcred->cmcred_groups[i] = p->p_ucred->cr_groups[i];
- return(0);
- }
+ for (i = 0; i < cmcred->cmcred_ngroups; i++)
+ cmcred->cmcred_groups[i] =
+ p->p_ucred->cr_groups[i];
+ break;
- oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
- /*
- * check that all the FDs passed in refer to legal OPEN files
- * If not, reject the entire operation.
- */
- fdp = (int *)(cm + 1);
- for (i = 0; i < oldfds; i++) {
- fd = *fdp++;
- if ((unsigned)fd >= fdescp->fd_nfiles ||
- fdescp->fd_ofiles[fd] == NULL)
- return (EBADF);
- }
- /*
- * Now replace the integer FDs with pointers to
- * the associated global file table entry..
- * Allocate a bigger buffer as necessary. But if an cluster is not
- * enough, return E2BIG.
- */
- newlen = CMSG_LEN(oldfds * sizeof(struct file *));
- if (newlen > MCLBYTES)
- return (E2BIG);
- if (newlen - control->m_len > M_TRAILINGSPACE(control)) {
- if (control->m_flags & M_EXT)
- return (E2BIG);
- MCLGET(control, M_TRYWAIT);
- if ((control->m_flags & M_EXT) == 0)
- return (ENOBUFS);
-
- /* copy the data to the cluster */
- memcpy(mtod(control, char *), cm, cm->cmsg_len);
- cm = mtod(control, struct cmsghdr *);
- }
+ case SCM_RIGHTS:
+ oldfds = datalen / sizeof (int);
+ /*
+ * check that all the FDs passed in refer to legal files
+ * If not, reject the entire operation.
+ */
+ fdp = data;
+ for (i = 0; i < oldfds; i++) {
+ fd = *fdp++;
+ if ((unsigned)fd >= fdescp->fd_nfiles ||
+ fdescp->fd_ofiles[fd] == NULL) {
+ error = EBADF;
+ goto out;
+ }
+ }
+ /*
+ * Now replace the integer FDs with pointers to
+ * the associated global file table entry..
+ */
+ newlen = oldfds * sizeof(struct file *);
+ *controlp = sbcreatecontrol(NULL, newlen,
+ SCM_RIGHTS, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = E2BIG;
+ goto out;
+ }
- /*
- * Adjust length, in case sizeof(struct file *) and sizeof(int)
- * differs.
- */
- control->m_len = cm->cmsg_len = newlen;
+ fdp = data;
+ rp = (struct file **)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ for (i = 0; i < oldfds; i++) {
+ fp = fdescp->fd_ofiles[*fdp++];
+ *rp++ = fp;
+ fp->f_count++;
+ fp->f_msgcount++;
+ unp_rights++;
+ }
+ break;
- /*
- * Transform the file descriptors into struct file pointers.
- * If sizeof (struct file *) is bigger than or equal to sizeof int,
- * then do it in reverse order so that the int won't get until
- * we're done.
- * If sizeof (struct file *) is smaller than sizeof int, then
- * do it in forward order.
- */
- if (sizeof (struct file *) >= sizeof (int)) {
- fdp = (int *)(cm + 1) + oldfds - 1;
- rp = (struct file **)CMSG_DATA(cm) + oldfds - 1;
- for (i = 0; i < oldfds; i++) {
- fp = fdescp->fd_ofiles[*fdp--];
- *rp-- = fp;
- fp->f_count++;
- fp->f_msgcount++;
- unp_rights++;
+ case SCM_TIMESTAMP:
+ *controlp = sbcreatecontrol(NULL, sizeof(*tv),
+ SCM_TIMESTAMP, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ tv = (struct timeval *)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ microtime(tv);
+ break;
+
+ default:
+ error = EINVAL;
+ goto out;
}
- } else {
- fdp = (int *)(cm + 1);
- rp = (struct file **)CMSG_DATA(cm);
- for (i = 0; i < oldfds; i++) {
- fp = fdescp->fd_ofiles[*fdp++];
- *rp++ = fp;
- fp->f_count++;
- fp->f_msgcount++;
- unp_rights++;
+
+ controlp = &(*controlp)->m_next;
+
+ if (CMSG_SPACE(datalen) < clen) {
+ clen -= CMSG_SPACE(datalen);
+ cm = (struct cmsghdr *)
+ ((caddr_t)cm + CMSG_SPACE(datalen));
+ } else {
+ clen = 0;
+ cm = NULL;
}
}
- return (0);
+
+out:
+ m_freem(control);
+
+ return (error);
}
static int unp_defer, unp_gcing;
@@ -1343,28 +1416,48 @@ unp_scan(m0, op)
register struct mbuf *m0;
void (*op) __P((struct file *));
{
- register struct mbuf *m;
- register struct file **rp;
- register struct cmsghdr *cm;
- register int i;
+ struct mbuf *m;
+ struct file **rp;
+ struct cmsghdr *cm;
+ void *data;
+ int i;
+ socklen_t clen, datalen;
int qfds;
while (m0) {
- for (m = m0; m; m = m->m_next)
- if (m->m_type == MT_CONTROL &&
- m->m_len >= sizeof(*cm)) {
- cm = mtod(m, struct cmsghdr *);
- if (cm->cmsg_level != SOL_SOCKET ||
- cm->cmsg_type != SCM_RIGHTS)
- continue;
- qfds = (cm->cmsg_len -
- (CMSG_DATA(cm) - (u_char *)cm))
- / sizeof (struct file *);
- rp = (struct file **)CMSG_DATA(cm);
- for (i = 0; i < qfds; i++)
- (*op)(*rp++);
- break; /* XXX, but saves time */
+ for (m = m0; m; m = m->m_next) {
+ if (m->m_type == MT_CONTROL)
+ continue;
+
+ cm = mtod(m, struct cmsghdr *);
+ clen = m->m_len;
+
+ while (cm != NULL) {
+ if (sizeof(*cm) > clen || cm->cmsg_len > clen)
+ break;
+
+ data = CMSG_DATA(cm);
+ datalen = (caddr_t)cm + cm->cmsg_len
+ - (caddr_t)data;
+
+ if (cm->cmsg_level == SOL_SOCKET &&
+ cm->cmsg_type == SCM_RIGHTS) {
+ qfds = datalen / sizeof (struct file *);
+ rp = data;
+ for (i = 0; i < qfds; i++)
+ (*op)(*rp++);
+ }
+
+ if (CMSG_SPACE(datalen) < clen) {
+ clen -= CMSG_SPACE(datalen);
+ cm = (struct cmsghdr *)
+ ((caddr_t)cm + CMSG_SPACE(datalen));
+ } else {
+ clen = 0;
+ cm = NULL;
+ }
}
+ }
m0 = m0->m_act;
}
}
OpenPOWER on IntegriCloud