⬢ DragonFlyBSD Kernel Audit
DF-5792 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/kern_descrip.c	2026-10-12 19:18:20.199758671 +0000
+++ b/sys/kern/kern_descrip.c	2026-10-12 19:18:20.219758409 +0000
@@ -3062,31 +3062,63 @@
 		}
 		fdtol = p->p_fdtol;
 		if (fdtol != NULL) {
+			struct filedesc *wfdp;
+			struct filedesc_to_leader *wfdtol;
+			struct filedesc_to_leader *next_fdtol;
+			struct filedesc_to_leader *wakeup_fdtol;
+
 			lwkt_gettoken(&p->p_token);
+			wfdp = p->p_fd;
 
 			/*
 			 * Handle special case where file descriptor table
 			 * is shared between multiple process leaders.
+			 *
+			 * fdfree() unlinks and frees entries under
+			 * wfdp->fd_spin and sleeps while fdl_holdcount is
+			 * non-zero.  Load the next entry and take a
+			 * holdcount on it under the same spinlock so the
+			 * entry cannot be freed out from under us while we
+			 * walk it.  The VOP may block; the holdcount keeps
+			 * the entry alive across it.
 			 */
-			for (fdtol = fdtol->fdl_next;
-			     fdtol != p->p_fdtol;
-			     fdtol = fdtol->fdl_next) {
-				if (fdtol->fdl_leader->p_advlock_flag == 0)
-					continue;
-				fdtol->fdl_holdcount++;
-				lf.l_whence = SEEK_SET;
-				lf.l_start = 0;
-				lf.l_len = 0;
-				lf.l_type = F_UNLCK;
-				vp = (struct vnode *)fp->f_data;
-				VOP_ADVLOCK(vp, (caddr_t)fdtol->fdl_leader,
-					    F_UNLCK, &lf, F_POSIX);
-				fdtol->fdl_holdcount--;
-				if (fdtol->fdl_holdcount == 0 &&
-				    fdtol->fdl_wakeup != 0) {
-					fdtol->fdl_wakeup = 0;
-					wakeup(fdtol);
+			spin_lock(&wfdp->fd_spin);
+			wfdtol = fdtol->fdl_next;
+			if (wfdtol != fdtol)
+				wfdtol->fdl_holdcount++;
+			spin_unlock(&wfdp->fd_spin);
+
+			while (wfdtol != fdtol) {
+				if (wfdtol->fdl_leader->p_advlock_flag) {
+					lf.l_whence = SEEK_SET;
+					lf.l_start = 0;
+					lf.l_len = 0;
+					lf.l_type = F_UNLCK;
+					vp = (struct vnode *)fp->f_data;
+					VOP_ADVLOCK(vp,
+						    (caddr_t)wfdtol->fdl_leader,
+						    F_UNLCK, &lf, F_POSIX);
+				}
+
+				wakeup_fdtol = NULL;
+				spin_lock(&wfdp->fd_spin);
+				/*
+				 * Load the next entry while we still hold
+				 * this one, then release our holdcount.
+				 */
+				next_fdtol = wfdtol->fdl_next;
+				if (--wfdtol->fdl_holdcount == 0 &&
+				    wfdtol->fdl_wakeup != 0) {
+					wfdtol->fdl_wakeup = 0;
+					wakeup_fdtol = wfdtol;
 				}
+				wfdtol = next_fdtol;
+				if (wfdtol != fdtol)
+					wfdtol->fdl_holdcount++;
+				spin_unlock(&wfdp->fd_spin);
+
+				if (wakeup_fdtol)
+					wakeup(wakeup_fdtol);
 			}
 			lwkt_reltoken(&p->p_token);
 		}
@@ -3356,10 +3388,16 @@
 	fdtol->fdl_wakeup = 0;
 	fdtol->fdl_leader = leader;
 	if (old != NULL) {
+		/*
+		 * Chain surgery must serialize against fdfree()'s unlink
+		 * and closef()'s walk, which use the same fd-table spin.
+		 */
+		spin_lock(&leader->p_fd->fd_spin);
 		fdtol->fdl_next = old->fdl_next;
 		fdtol->fdl_prev = old;
 		old->fdl_next = fdtol;
 		fdtol->fdl_next->fdl_prev = fdtol;
+		spin_unlock(&leader->p_fd->fd_spin);
 	} else {
 		fdtol->fdl_next = fdtol;
 		fdtol->fdl_prev = fdtol;