[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: UsePrivilegeSeparation, 2.9/sparc?



Theo de Raadt <deraadt@cvs.openbsd.org> writes:

> We have a bug in 2.9 and 3.0; big-endian machines had broken support
> for fd passing, which must be fixed.  I've asked Art to roll a patch
> for this.  It's not easy to roll a little patch for this, but he groks
> the importance for some of you..  so hang on.

This is a diff for 2.9, should be easy to adapt to 3.0. I'm not completly
sure that the diff is ok, but it should be good enough for testing.

//art

Index: sys/un.h
===================================================================
RCS file: /cvs/src/sys/sys/un.h,v
retrieving revision 1.3
diff -u -r1.3 un.h
--- sys/un.h	5 Mar 1998 19:47:52 -0000	1.3
+++ sys/un.h	26 Jun 2002 10:48:43 -0000
@@ -62,7 +62,7 @@
 void	unp_drop __P((struct unpcb *unp, int errno));
 void	unp_gc __P((void));
 void	unp_mark __P((struct file *fp));
-void	unp_scan __P((struct mbuf *m0, void (*op) __P((struct file *))));
+void	unp_scan __P((struct mbuf *m0, void (*op) __P((struct file *)), int));
 void	unp_shutdown __P((struct unpcb *unp));
 int 	unp_externalize __P((struct mbuf *));
 int	unp_internalize __P((struct mbuf *, struct proc *));
Index: kern/uipc_syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.36.2.1
diff -u -r1.36.2.1 uipc_syscalls.c
--- kern/uipc_syscalls.c	2 Dec 2001 18:25:32 -0000	1.36.2.1
+++ kern/uipc_syscalls.c	26 Jun 2002 10:48:43 -0000
@@ -1,4 +1,4 @@
-/*	$OpenBSD: uipc_syscalls.c,v 1.36.2.1 2001/12/02 18:25:32 miod Exp $	*/
+/*	$OpenBSD: uipc_syscalls.c,v 1.49 2002/02/11 12:34:30 art Exp $	*/
 /*	$NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $	*/
 
 /*
@@ -398,7 +398,7 @@
 	error = copyin(SCARG(uap, msg), (caddr_t)&msg, sizeof (msg));
 	if (error)
 		return (error);
-	if (msg.msg_iovlen <= 0 || msg.msg_iovlen > IOV_MAX)
+	if (msg.msg_iovlen < 0 || msg.msg_iovlen > IOV_MAX)
 		return (EMSGSIZE);
 	if (msg.msg_iovlen > UIO_SMALLIOV)
 		iov = malloc(sizeof(struct iovec) * msg.msg_iovlen,
@@ -575,13 +575,13 @@
 	} */ *uap = v;
 	struct msghdr msg;
 	struct iovec aiov[UIO_SMALLIOV], *uiov, *iov;
-	register int error;
+	int error;
 
 	error = copyin((caddr_t)SCARG(uap, msg), (caddr_t)&msg,
 		       sizeof (msg));
 	if (error)
 		return (error);
-	if (msg.msg_iovlen <= 0 || msg.msg_iovlen > IOV_MAX)
+	if (msg.msg_iovlen < 0 || msg.msg_iovlen > IOV_MAX)
 		return (EMSGSIZE);
 	if (msg.msg_iovlen > UIO_SMALLIOV)
 		iov = malloc(sizeof(struct iovec) * msg.msg_iovlen,
@@ -593,12 +593,14 @@
 #else
 	msg.msg_flags = SCARG(uap, flags);
 #endif
+ 	if (msg.msg_iovlen > 0) {
+ 		error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
+ 		    (unsigned)(msg.msg_iovlen * sizeof (struct iovec)));
+ 		if (error)
+ 			goto done;
+ 	}
 	uiov = msg.msg_iov;
 	msg.msg_iov = iov;
-	error = copyin((caddr_t)uiov, (caddr_t)iov,
-		       (unsigned)(msg.msg_iovlen * sizeof (struct iovec)));
-	if (error)
-		goto done;
 	if ((error = recvit(p, SCARG(uap, s), &msg, (caddr_t)0, retval)) == 0) {
 		msg.msg_iov = uiov;
 		error = copyout((caddr_t)&msg, (caddr_t)SCARG(uap, msg),
Index: kern/uipc_usrreq.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_usrreq.c,v
retrieving revision 1.12
diff -u -r1.12 uipc_usrreq.c
--- kern/uipc_usrreq.c	6 Apr 2001 04:42:07 -0000	1.12
+++ kern/uipc_usrreq.c	26 Jun 2002 10:48:44 -0000
@@ -1,4 +1,4 @@
-/*	$OpenBSD: uipc_usrreq.c,v 1.12 2001/04/06 04:42:07 csapuntz Exp $	*/
+/*	$OpenBSD: uipc_usrreq.c,v 1.21 2002/03/14 01:27:05 millert Exp $	*/
 /*	$NetBSD: uipc_usrreq.c,v 1.18 1996/02/09 19:00:50 christos Exp $	*/
 
 /*
@@ -611,32 +611,81 @@
 	struct mbuf *rights;
 {
 	struct proc *p = curproc;		/* XXX */
-	register struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
-	struct file **rp = (struct file **)(cm + 1), *fp;
-	int newfds = (cm->cmsg_len - sizeof(*cm)) / sizeof (int);
-	int i, f, *ip;
-
-	if (!fdavail(p, newfds)) {
-		for (i = 0; i < newfds; i++) {
-			fp = *rp;
-			unp_discard(fp);
-			*rp++ = 0;
-		}
-		return (EMSGSIZE);
-	}
-	ip = (int *)rp;
-	for (i = 0; i < newfds; i++) {
-		if (fdalloc(p, 0, &f))
-			panic("unp_externalize");
-		bcopy(rp, &fp, sizeof fp);
+	struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
+	int i, *fdp;
+	struct file **rp;
+	struct file *fp;
+	int nfds, error = 0;
+
+	nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) /
+	    sizeof(struct file *);
+	rp = (struct file **)CMSG_DATA(cm);
+
+	fdp = malloc(nfds * sizeof(int), M_TEMP, M_WAITOK);
+
+	/*
+	 * First loop -- allocate file descriptor table slots for the
+	 * new descriptors.
+	 */
+	rp = ((struct file **)CMSG_DATA(cm));
+	for (i = 0; i < nfds; i++) {
+		bcopy(rp, &fp, sizeof(fp));
 		rp++;
-		p->p_fd->fd_ofiles[f] = fp;
+		if ((error = fdalloc(p, 0, &fdp[i])) != 0) {
+			/*
+			 * Back out what we've done so far.
+			 */
+			for (--i; i >= 0; i--)
+				fdremove(p->p_fd, fdp[i]);
+
+			/*
+			 * This is the error that has historically
+			 * been returned, and some callers may
+			 * expect it.
+			 */
+			error = EMSGSIZE;
+			rp = ((struct file **)CMSG_DATA(cm));
+			for (i = 0; i < nfds; i++) {
+				fp = *rp;
+				/*
+				 * zero the pointer before calling unp_discard,
+				 * since it may end up in unp_gc()..
+				 */
+				*rp++ = 0;
+				unp_discard(fp);
+			}
+			goto out;
+		}
+
+		/*
+		 * Make the slot reference the descriptor so that
+		 * fdalloc() works properly.. We finalize it all
+		 * in the loop below.
+		 */
+		p->p_fd->fd_ofiles[fdp[i]] = fp;
+	}
+
+	/*
+	 * Now that adding them has succeeded, update all of the
+	 * descriptor passing state.
+	 */
+	rp = (struct file **)CMSG_DATA(cm);
+	for (i = 0; i < nfds; i++) {
+		fp = *rp++;
 		fp->f_msgcount--;
 		unp_rights--;
-		bcopy(&f, ip, sizeof f);
-		ip++;
 	}
-	return (0);
+
+	/*
+	 * Copy temporary array to message and adjust length, in case of
+	 * transition from large struct file pointers to ints.
+	 */
+	memcpy(CMSG_DATA(cm), fdp, nfds * sizeof(int));
+	cm->cmsg_len = CMSG_LEN(nfds * sizeof(int));
+	rights->m_len = CMSG_SPACE(nfds * sizeof(int));
+ out:
+	free(fdp, M_TEMP);
+	return (error);
 }
 
 int
@@ -645,48 +694,75 @@
 	struct proc *p;
 {
 	struct filedesc *fdp = p->p_fd;
-	register struct cmsghdr *cm = mtod(control, struct cmsghdr *);
+	struct cmsghdr *cm = mtod(control, struct cmsghdr *);
 	struct file **rp, *fp;
-	register int i;
+	int i, error;
 	struct mbuf *n = NULL;
-	int oldfds, *ip, fd;
+	int nfds, *ip, fd, neededspace;
 
 	if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
 	    cm->cmsg_len != control->m_len)
 		return (EINVAL);
-	oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
-	ip = (int *)(cm + 1);
-	for (i = 0; i < oldfds; i++) {
-		fd = *ip++;
-		if ((unsigned)fd >= fdp->fd_nfiles ||
-		    fdp->fd_ofiles[fd] == NULL)
-			return (EBADF);
-		if (fdp->fd_ofiles[fd]->f_count == LONG_MAX-2 ||
-		    fdp->fd_ofiles[fd]->f_msgcount == LONG_MAX-2)
-			return (EDEADLK);
-	}
-	ip = (int *)(cm + 1);
-	if (sizeof(int) != sizeof(struct file *)) {
-		MGET(n, M_WAIT, MT_DATA);
-		rp = (struct file **)mtod(n, caddr_t);
-	} else
-		rp = (struct file **)ip;
-	for (i = 0; i < oldfds; i++) {
+	nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) / sizeof (int);
+
+	/* 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)
+			return (E2BIG);
+
+		/* allocate a cluster and try again */
+		MCLGET(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;
+	}
+
+	/* 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 *));
+
+	ip = ((int *)CMSG_DATA(cm)) + nfds - 1;
+	rp = ((struct file **)CMSG_DATA(cm)) + nfds - 1;
+	for (i = 0; i < nfds; i++) {
 		bcopy(ip, &fd, sizeof fd);
-		ip++;
-		fp = fdp->fd_ofiles[fd];
+		ip--;
+		if ((fp = fdp->fd_ofiles[fd]) == NULL) {
+			error = EBADF;
+			goto fail;
+		}
+		if (fp->f_count == LONG_MAX-2 ||
+		    fp->f_msgcount == LONG_MAX-2) {
+			error = EDEADLK;
+			goto fail;
+		}
 		bcopy(&fp, rp, sizeof fp);
-		rp++;
+		rp--;
 		fp->f_count++;
 		fp->f_msgcount++;
 		unp_rights++;
 	}
-	if (n) {
-		m_adj(control, -(oldfds * sizeof(int)));
-		n->m_len = oldfds * sizeof(struct file *);
-		m_cat(control, n);
-	}
 	return (0);
+fail:
+	/* Back out what we just did. */
+	for ( ; i > 0; i--) {
+		bcopy(rp, &fp, sizeof(fp));
+		rp++;
+		fp->f_count--;
+		fp->f_msgcount--;
+		unp_rights--;
+	}
+	if (n)
+		m_freem(n);
+
+	return (error);
 }
 
 int	unp_defer, unp_gcing;
@@ -708,18 +784,19 @@
 		fp->f_flag &= ~(FMARK|FDEFER);
 	do {
 		for (fp = filehead.lh_first; fp != 0; fp = fp->f_list.le_next) {
-			if (fp->f_count == 0)
-				continue;
 			if (fp->f_flag & FDEFER) {
 				fp->f_flag &= ~FDEFER;
 				unp_defer--;
 			} else {
+				if (fp->f_count == 0)
+					continue;
 				if (fp->f_flag & FMARK)
 					continue;
 				if (fp->f_count == fp->f_msgcount)
 					continue;
-				fp->f_flag |= FMARK;
 			}
+			fp->f_flag |= FMARK;
+
 			if (fp->f_type != DTYPE_SOCKET ||
 			    (so = (struct socket *)fp->f_data) == 0)
 				continue;
@@ -742,7 +819,7 @@
 				goto restart;
 			}
 #endif
-			unp_scan(so->so_rcv.sb_mb, unp_mark);
+			unp_scan(so->so_rcv.sb_mb, unp_mark, 0);
 		}
 	} while (unp_defer);
 	/*
@@ -800,7 +877,7 @@
 	        if ((*fpp)->f_type == DTYPE_SOCKET && (*fpp)->f_data != NULL)
 		        sorflush((struct socket *)(*fpp)->f_data);
 	for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
-		(void) closef(*fpp, (struct proc *)0);
+		(void) closef(*fpp, NULL);
 	free((caddr_t)extra_ref, M_FILE);
 	unp_gcing = 0;
 }
@@ -811,38 +888,43 @@
 {
 
 	if (m)
-		unp_scan(m, unp_discard);
+		unp_scan(m, unp_discard, 1);
 }
 
 void
-unp_scan(m0, op)
-	register struct mbuf *m0;
-	void (*op) __P((struct file *));
+unp_scan(m0, op, discard)
+	struct mbuf *m0;
+	void (*op)(struct file *);
+	int discard;
 {
-	register struct mbuf *m;
+	struct mbuf *m;
 	struct file **rp, *fp;
-	register struct cmsghdr *cm;
-	register int i;
+	struct cmsghdr *cm;
+	int i;
 	int qfds;
 
 	while (m0) {
-		for (m = m0; m; m = m->m_next)
+		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 - sizeof *cm) / sizeof (int);
-				rp = (struct file **)(cm + 1);
+				qfds = (cm->cmsg_len - CMSG_ALIGN(sizeof *cm))
+				    / sizeof(struct file *);
+				rp = (struct file **)CMSG_DATA(cm);
 				for (i = 0; i < qfds; i++) {
-					bcopy(rp, &fp, sizeof fp);
-					rp++;
+					fp = *rp;
+					if (discard)
+						*rp = 0;
 					(*op)(fp);
+					rp++;
 				}
 				break;		/* XXX, but saves time */
 			}
-		m0 = m0->m_act;
+		}
+		m0 = m0->m_nextpkt;
 	}
 }
 
@@ -853,8 +935,16 @@
 
 	if (fp->f_flag & FMARK)
 		return;
-	unp_defer++;
-	fp->f_flag |= (FMARK|FDEFER);
+
+	if (fp->f_flag & FDEFER)
+		return;
+
+	if (fp->f_type == DTYPE_SOCKET) {
+		unp_defer++;
+		fp->f_flag |= FDEFER;
+	} else {
+		fp->f_flag |= FMARK;
+	}
 }
 
 void
@@ -862,7 +952,9 @@
 	struct file *fp;
 {
 
+	if (fp == NULL)
+		return;
 	fp->f_msgcount--;
 	unp_rights--;
-	(void) closef(fp, (struct proc *)0);
+	(void) closef(fp, NULL);
 }