Index: sys/kern/kern_descrip.c =================================================================== --- sys/kern/kern_descrip.c +++ sys/kern/kern_descrip.c @@ -2153,6 +2153,9 @@ if (fcaps != NULL) filecaps_validate(fcaps, __func__); FILEDESC_XLOCK_ASSERT(fdp); + KASSERT(refcount_load(&fdp->fd_refcnt) == 1 || + (fp->f_ops->fo_flags & DFLAG_PASSABLE) != 0, + ("%s: file %p cannot be shared", __func__, fp)); fde = &fdp->fd_ofiles[fd]; #ifdef CAPABILITIES @@ -2179,10 +2182,13 @@ MPASS(fd != NULL); FILEDESC_XLOCK(fdp); - error = fdalloc(td, 0, fd); - if (__predict_true(error == 0)) { + if (__predict_false(refcount_load(&fdp->fd_refcnt) > 1) && + (fp->f_ops->fo_flags & DFLAG_PASSABLE) == 0) + error = EOPNOTSUPP; + else + error = fdalloc(td, 0, fd); + if (__predict_true(error == 0)) _finstall(fdp, fp, *fd, flags, fcaps); - } FILEDESC_XUNLOCK(fdp); return (error); } @@ -2336,14 +2342,38 @@ } /* - * Share a filedesc structure. + * Share a filedesc structure (among multiple processes). Descriptors that + * cannot be passed among processes may not exist in a shared table. */ struct filedesc * fdshare(struct filedesc *fdp) { + struct file *fp; + int i; + bool ok; - refcount_acquire(&fdp->fd_refcnt); - return (fdp); + ok = true; + FILEDESC_SLOCK(fdp); + FILEDESC_FOREACH_FP(fdp, i, fp) { + if ((fp->f_ops->fo_flags & DFLAG_PASSABLE) == 0) { + ok = false; + break; + } + } + if (ok) + refcount_acquire(&fdp->fd_refcnt); + FILEDESC_SUNLOCK(fdp); + return (ok ? fdp : NULL); +} + +void +fdshare_abort(struct filedesc *fdp) +{ + bool released __diagused; + + released = refcount_release(&fdp->fd_refcnt); + KASSERT(!released, + ("fdshare_abort: released last reference on %p", fdp)); } /* Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c +++ sys/kern/kern_fork.c @@ -442,7 +442,9 @@ pd = pdcopy(p1->p_pd); else pd = pdshare(p1->p_pd); - fd = fdshare(p1->p_fd); + + /* FD table is already shared by the caller. */ + fd = p1->p_fd; if (p1->p_fdtol == NULL) p1->p_fdtol = filedesc_to_leader_alloc(NULL, NULL, p1->p_leader); @@ -867,6 +869,7 @@ struct vmspace *vm2; struct ucred *cred; struct file *fp_procdesc; + struct filedesc *fdp; vm_ooffset_t mem_charged; int error, nprocs_new; static int curfail; @@ -925,10 +928,21 @@ return (fork_norfproc(td, flags)); } + fdp = NULL; fp_procdesc = NULL; newproc = NULL; vm2 = NULL; + /* + * Take an extra reference to the file descriptor table if the table is + * to be shared. + */ + if ((flags & RFFDG) == 0) { + fdp = fdshare(td->td_proc->p_fd); + if (fdp == NULL) + return (EOPNOTSUPP); + } + /* * Increment the nprocs resource before allocations occur. * Although process entries are dynamically created, we still @@ -1060,6 +1074,8 @@ fail2: if (vm2 != NULL) vmspace_free(vm2); + if (fdp != NULL) + fdshare_abort(fdp); uma_zfree(proc_zone, newproc); if ((flags & RFPROCDESC) != 0 && fp_procdesc != NULL) { fdclose(td, fp_procdesc, *fr->fr_pd_fd); Index: sys/sys/filedesc.h =================================================================== --- sys/sys/filedesc.h +++ sys/sys/filedesc.h @@ -115,7 +115,7 @@ struct fdescenttbl *fd_files; /* open files table */ NDSLOTTYPE *fd_map; /* bitmap of free fds */ int fd_freefile; /* approx. next free file */ - int fd_refcnt; /* thread reference count */ + int fd_refcnt; /* process reference count */ int fd_holdcnt; /* hold count on structure + mutex */ struct sx fd_sx; /* protects members of this struct */ struct kqlist fd_kqlist; /* list of kqueues on this filedesc */ @@ -267,6 +267,7 @@ int fdlastfile_single(struct filedesc *fdp); struct filedesc *fdinit(void); struct filedesc *fdshare(struct filedesc *fdp); +void fdshare_abort(struct filedesc *fdp); struct filedesc_to_leader * filedesc_to_leader_alloc(struct filedesc_to_leader *old, struct filedesc *fdp, struct proc *leader);