DF-5792 / fix.diff
--- 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; |