From: Vitaliy Makkoveev Subject: unix(4): reduce kernel lock pressure within unp_externalize() To: tech@openbsd.org Date: Wed, 3 Apr 2024 17:56:39 +0300 Kernel lock required for `fd_rdir' dereference and the following vn_isunder() call only while passing directory. This is not the most common case and there is no reason to stuck in lock while passing descriptors. In the original code, kernel lock doesn't protect `fd_rdir' atomicy because it could be changed during vn_isunder(). Also it could be changed just after kernel lock release after the for loop. Index: sys/kern/uipc_usrreq.c =================================================================== RCS file: /cvs/src/sys/kern/uipc_usrreq.c,v retrieving revision 1.203 diff -u -p -r1.203 uipc_usrreq.c --- sys/kern/uipc_usrreq.c 26 Mar 2024 09:46:47 -0000 1.203 +++ sys/kern/uipc_usrreq.c 3 Apr 2024 14:41:40 -0000 @@ -1060,36 +1060,40 @@ unp_externalize(struct mbuf *rights, soc /* Make sure the recipient should be able to see the descriptors.. */ rp = (struct fdpass *)CMSG_DATA(cm); - /* fdp->fd_rdir requires KERNEL_LOCK() */ - KERNEL_LOCK(); - for (i = 0; i < nfds; i++) { fp = rp->fp; rp++; error = pledge_recvfd(p, fp); if (error) - break; + goto out; /* * No to block devices. If passing a directory, * make sure that it is underneath the root. */ - if (fdp->fd_rdir != NULL && fp->f_type == DTYPE_VNODE) { + + if (fp->f_type == DTYPE_VNODE) { struct vnode *vp = (struct vnode *)fp->f_data; - if (vp->v_type == VBLK || - (vp->v_type == VDIR && - !vn_isunder(vp, fdp->fd_rdir, p))) { + switch (vp->v_type) { + case VDIR: + KERNEL_LOCK(); + if (fdp->fd_rdir && + vn_isunder(vp, fdp->fd_rdir, p)) { + KERNEL_UNLOCK(); + break; + } + KERNEL_UNLOCK(); + + /* FALLTHROUGH */ + case VBLK: error = EPERM; + goto out; + default: break; } } } - - KERNEL_UNLOCK(); - - if (error) - goto out; fds = mallocarray(nfds, sizeof(int), M_TEMP, M_WAITOK);