Download raw body.
unix(4): reduce kernel lock pressure within unp_externalize()
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);
unix(4): reduce kernel lock pressure within unp_externalize()