From f2a172afbf75ccd2dd8a025ee300356bde0a75b3 Mon Sep 17 00:00:00 2001 From: christos Date: Sat, 6 Oct 2012 22:58:08 +0000 Subject: [PATCH] Avoid crash dereferencing a NULL fp in fd_affix() in unp_externalize caused by the sequence of passing two fd's with two sendmsg()'s, then doing a read() and a recvmsg(). The read() calls dom_dispose() which discards both messages in the mbuf, and sets the fp's in the array to NULL. Linux dequeues only one message per read() so the second recvmsg() gets the fd from the second message. This fix just avoids the NULL pointer de-reference, making the second recvmsg() to fail. It is dubious to pass fd's with stream sockets and expect mixing read() and recvmsg() to work. Plus processing one control message per read() changes the current semantics and should be examined before applied. In addition there is a race between dom_externalize() and dom_dispose(): what happens in a multi-threaded network stack when one thread disposes where the other externalizes the same array? NB: Pullup to 6. --- sys/kern/uipc_usrreq.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index c82282e7153c..57e42b191c0b 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -1,4 +1,4 @@ -/* $NetBSD: uipc_usrreq.c,v 1.139 2012/07/30 10:45:03 christos Exp $ */ +/* $NetBSD: uipc_usrreq.c,v 1.140 2012/10/06 22:58:08 christos Exp $ */ /*- * Copyright (c) 1998, 2000, 2004, 2008, 2009 The NetBSD Foundation, Inc. @@ -96,7 +96,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: uipc_usrreq.c,v 1.139 2012/07/30 10:45:03 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uipc_usrreq.c,v 1.140 2012/10/06 22:58:08 christos Exp $"); #include #include @@ -1247,29 +1247,30 @@ unp_externalize(struct mbuf *rights, struct lwp *l, int flags) rw_enter(&p->p_cwdi->cwdi_lock, RW_READER); /* Make sure the recipient should be able to see the files.. */ - if (p->p_cwdi->cwdi_rdir != NULL) { - rp = (file_t **)CMSG_DATA(cm); - for (size_t i = 0; i < nfds; i++) { - file_t * const fp = *rp++; - /* - * If we are in a chroot'ed directory, and - * someone wants to pass us a directory, make - * sure it's inside the subtree we're allowed - * to access. - */ - if (fp->f_type == DTYPE_VNODE) { - vnode_t *vp = (vnode_t *)fp->f_data; - if ((vp->v_type == VDIR) && - !vn_isunder(vp, p->p_cwdi->cwdi_rdir, l)) { - error = EPERM; - goto out; - } + rp = (file_t **)CMSG_DATA(cm); + for (size_t i = 0; i < nfds; i++) { + file_t * const fp = *rp++; + if (fp == NULL) { + error = EINVAL; + goto out; + } + /* + * If we are in a chroot'ed directory, and + * someone wants to pass us a directory, make + * sure it's inside the subtree we're allowed + * to access. + */ + if (p->p_cwdi->cwdi_rdir != NULL && fp->f_type == DTYPE_VNODE) { + vnode_t *vp = (vnode_t *)fp->f_data; + if ((vp->v_type == VDIR) && + !vn_isunder(vp, p->p_cwdi->cwdi_rdir, l)) { + error = EPERM; + goto out; } } } restart: - rp = (file_t **)CMSG_DATA(cm); /* * First loop -- allocate file descriptor table slots for the * new files.