Index: sys/kern/kern_descrip.c =================================================================== --- sys/kern/kern_descrip.c +++ sys/kern/kern_descrip.c @@ -853,6 +853,10 @@ goto unlock; } + oldfde = &fdp->fd_ofiles[old]; + if (!fhold(oldfde->fde_file)) + goto unlock; + /* * If the caller specified a file descriptor, make sure the file * table is large enough to hold it, and grab it. Otherwise, just @@ -861,13 +865,17 @@ switch (mode) { case FDDUP_NORMAL: case FDDUP_FCNTL: - if ((error = fdalloc(td, new, &new)) != 0) + if ((error = fdalloc(td, new, &new)) != 0) { + fdrop(oldfde->fde_file, td); goto unlock; + } break; case FDDUP_MUSTREPLACE: /* Target file descriptor must exist. */ - if (fget_locked(fdp, new) == NULL) + if (fget_locked(fdp, new) == NULL) { + fdrop(oldfde->fde_file, td); goto unlock; + } break; case FDDUP_FIXED: if (new >= fdp->fd_nfiles) { @@ -884,6 +892,7 @@ error = racct_set_unlocked(p, RACCT_NOFILE, new + 1); if (error != 0) { error = EMFILE; + fdrop(oldfde->fde_file, td); goto unlock; } } @@ -899,8 +908,6 @@ KASSERT(old != new, ("new fd is same as old")); - oldfde = &fdp->fd_ofiles[old]; - fhold(oldfde->fde_file); newfde = &fdp->fd_ofiles[new]; delfp = newfde->fde_file; @@ -1902,11 +1909,15 @@ MPASS(fd != NULL); FILEDESC_XLOCK(fdp); + if (!fhold(fp)) { + FILEDESC_XUNLOCK(fdp); + return (EBADF); + } if ((error = fdalloc(td, 0, fd))) { + fdrop(fp, td); FILEDESC_XUNLOCK(fdp); return (error); } - fhold(fp); _finstall(fdp, fp, *fd, flags, fcaps); FILEDESC_XUNLOCK(fdp); return (0); @@ -2047,7 +2058,8 @@ for (i = 0; i <= fdp->fd_lastfile; ++i) { ofde = &fdp->fd_ofiles[i]; if (ofde->fde_file == NULL || - (ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0) { + (ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0 || + !fhold(ofde->fde_file)) { if (newfdp->fd_freefile == -1) newfdp->fd_freefile = i; continue; @@ -2055,7 +2067,6 @@ nfde = &newfdp->fd_ofiles[i]; *nfde = *ofde; filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); - fhold(nfde->fde_file); fdused_init(newfdp, i); newfdp->fd_lastfile = i; } @@ -2108,10 +2119,13 @@ error = EINVAL; goto bad; } + if (!fhold(nfde->fde_file)) { + error = EBADF; + goto bad; + } nfde = &newfdp->fd_ofiles[i]; *nfde = *ofde; filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); - fhold(nfde->fde_file); fdused_init(newfdp, i); newfdp->fd_lastfile = i; } @@ -2153,9 +2167,9 @@ (p->p_leader->p_flag & P_ADVLOCK) != 0) { for (i = 0; i <= fdp->fd_lastfile; i++) { fp = fdp->fd_ofiles[i].fde_file; - if (fp == NULL || fp->f_type != DTYPE_VNODE) + if (fp == NULL || fp->f_type != DTYPE_VNODE || + !fhold(fp)) continue; - fhold(fp); FILEDESC_XUNLOCK(fdp); lf.l_whence = SEEK_SET; lf.l_start = 0; @@ -2596,8 +2610,8 @@ get_locked: FILEDESC_SLOCK(fdp); error = fget_cap_locked(fdp, fd, needrightsp, fpp, havecapsp); - if (error == 0) - fhold(*fpp); + if (error == 0 && !fhold(*fpp)) + error = EBADF; FILEDESC_SUNLOCK(fdp); #endif return (error); @@ -2656,9 +2670,13 @@ * table before this fd was closed, so it possible that * there is a stale fp pointer in cached version. */ - fdt = *(const struct fdescenttbl * const volatile *)&(fdp->fd_files); + fdt = *(const struct fdescenttbl * const volatile *) + &(fdp->fd_files); continue; } + if (count + 1 < count) + return (EBADF); + /* * Use an acquire barrier to force re-reading of fdt so it is * refreshed for verification. @@ -3048,7 +3066,11 @@ FILEDESC_XUNLOCK(fdp); return (EACCES); } - fhold(fp); + if (!fhold(fp)) { + fdunused(fdp, indx); + FILEDESC_XUNLOCK(fdp); + return (EBADF); + } newfde = &fdp->fd_ofiles[indx]; oldfde = &fdp->fd_ofiles[dfd]; ioctls = filecaps_copy_prep(&oldfde->fde_caps); Index: sys/kern/sys_generic.c =================================================================== --- sys/kern/sys_generic.c +++ sys/kern/sys_generic.c @@ -757,7 +757,11 @@ fp = NULL; /* fhold() was not called yet */ goto out; } - fhold(fp); + if (!fhold(fp)) { + error = EBADF; + fp = NULL; + goto out; + } if (locked == LA_SLOCKED) { FILEDESC_SUNLOCK(fdp); locked = LA_UNLOCKED; Index: sys/kern/uipc_usrreq.c =================================================================== --- sys/kern/uipc_usrreq.c +++ sys/kern/uipc_usrreq.c @@ -2133,7 +2133,7 @@ struct file *fp; struct timeval *tv; struct timespec *ts; - int i, *fdp; + int i, j, *fdp; void *data; socklen_t clen = control->m_len, datalen; int error, oldfds; @@ -2218,6 +2218,19 @@ CMSG_DATA(mtod(*controlp, struct cmsghdr *)); fdev = malloc(sizeof(*fdev) * oldfds, M_FILECAPS, M_WAITOK); + for (i = 0; i < oldfds; i++, fdp++) { + if (!fhold(fdesc->fd_ofiles[*fdp].fde_file)) { + fdp = data; + for (j = 0; j < i; j++, fdp++) { + fdrop(fdesc->fd_ofiles[*fdp]. + fde_file, td); + } + FILEDESC_SUNLOCK(fdesc); + error = EBADF; + goto out; + } + } + fdp = data; for (i = 0; i < oldfds; i++, fdev++, fdp++) { fde = &fdesc->fd_ofiles[*fdp]; fdep[i] = fdev; @@ -2415,7 +2428,6 @@ unp->unp_file = fp; unp->unp_msgcount++; } - fhold(fp); unp_rights++; UNP_LINK_WUNLOCK(); } @@ -2576,10 +2588,10 @@ if ((unp->unp_gcflag & UNPGC_DEAD) != 0) { f = unp->unp_file; if (unp->unp_msgcount == 0 || f == NULL || - f->f_count != unp->unp_msgcount) + f->f_count != unp->unp_msgcount || + !fhold(f)) continue; unref[total++] = f; - fhold(f); KASSERT(total <= unp_unreachable, ("unp_gc: incorrect unreachable count.")); } Index: sys/sys/file.h =================================================================== --- sys/sys/file.h +++ sys/sys/file.h @@ -284,8 +284,12 @@ return (0); } -#define fhold(fp) \ - (refcount_acquire(&(fp)->f_count)) +static __inline bool +fhold(struct file *fp) +{ + return (refcount_acquire_checked(&fp->f_count)); +} + #define fdrop(fp, td) \ (refcount_release(&(fp)->f_count) ? _fdrop((fp), (td)) : _fnoop()) Index: sys/sys/refcount.h =================================================================== --- sys/sys/refcount.h +++ sys/sys/refcount.h @@ -54,6 +54,19 @@ atomic_add_int(count, 1); } +static __inline bool +refcount_acquire_checked(volatile u_int *count) +{ + u_int lcount; + + for (lcount = *count;;) { + if (lcount + 1 < lcount) + return (false); + if (atomic_fcmpset_int(count, &lcount, lcount + 1) == 1) + return (true); + } +} + static __inline int refcount_release(volatile u_int *count) {