diff --git a/sys/cam/ctl/ctl_backend_block.c b/sys/cam/ctl/ctl_backend_block.c --- a/sys/cam/ctl/ctl_backend_block.c +++ b/sys/cam/ctl/ctl_backend_block.c @@ -2541,7 +2541,7 @@ "Root filesystem is not mounted"); return (1); } - pwd_ensure_dirs(); + pwd_ensure_dirs(curproc); value = dnvlist_get_string(cbe_lun->options, "file", NULL); if (value == NULL) { diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c @@ -57,7 +57,7 @@ int error; td = curthread; - pwd_ensure_dirs(); + pwd_ensure_dirs(curproc); KASSERT((flags & (O_EXEC | O_PATH)) == 0, ("invalid flags: 0x%x", flags)); diff --git a/sys/dev/xen/blkback/blkback.c b/sys/dev/xen/blkback/blkback.c --- a/sys/dev/xen/blkback/blkback.c +++ b/sys/dev/xen/blkback/blkback.c @@ -2522,7 +2522,7 @@ if ((xbb->flags & XBBF_READ_ONLY) == 0) flags |= FWRITE; - pwd_ensure_dirs(); + pwd_ensure_dirs(curproc); again: NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, xbb->dev_name); diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -4272,15 +4272,15 @@ * mac_vnode_check_chroot() to authorize this operation. */ int -pwd_chroot(struct thread *td, struct vnode *vp) +pwd_chroot(struct proc *p, struct vnode *vp) { struct pwddesc *pdp; struct filedesc *fdp; struct pwd *newpwd, *oldpwd; int error; - fdp = td->td_proc->p_fd; - pdp = td->td_proc->p_pd; + fdp = p->p_fd; + pdp = p->p_pd; newpwd = pwd_alloc(); FILEDESC_SLOCK(fdp); PWDDESC_XLOCK(pdp); @@ -4315,7 +4315,7 @@ } void -pwd_chdir(struct thread *td, struct vnode *vp) +pwd_chdir(struct proc *p, struct vnode *vp) { struct pwddesc *pdp; struct pwd *newpwd, *oldpwd; @@ -4323,7 +4323,7 @@ VNPASS(vp->v_usecount > 0, vp); newpwd = pwd_alloc(); - pdp = td->td_proc->p_pd; + pdp = p->p_pd; PWDDESC_XLOCK(pdp); oldpwd = PWDDESC_XLOCKED_LOAD_PWD(pdp); newpwd->pwd_cdir = vp; @@ -4410,12 +4410,12 @@ } void -pwd_ensure_dirs(void) +pwd_ensure_dirs(struct proc *p) { struct pwddesc *pdp; struct pwd *oldpwd, *newpwd; - pdp = curproc->p_pd; + pdp = p->p_pd; PWDDESC_XLOCK(pdp); oldpwd = PWDDESC_XLOCKED_LOAD_PWD(pdp); if (oldpwd->pwd_cdir != NULL && oldpwd->pwd_rdir != NULL && diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -441,13 +441,63 @@ int pgid; /* target pgrp id */ }; #endif + +/* + * Core of setpgid(2): move targp into the process group pgid (0 selects a new + * group whose id is targp's own pid), enforcing the session and group-leader + * rules. The caller holds proctree_lock, has resolved targp (by pid or by + * process descriptor), and supplies a preallocated group in + * *newpgrpp; when a fresh group is created that pgrp is consumed and *newpgrpp + * is set to NULL. A return of ERESTART asks the caller to drop the lock and + * retry. + * + * Factored out of sys_setpgid() so the group-change core can be shared with a + * caller that resolves targp differently. + */ +static int +do_setpgid(struct proc *curp, struct proc *targp, pid_t pgid, + struct pgrp **newpgrpp) +{ + struct pgrp *pgrp; + int error; + + sx_assert(&proctree_lock, SX_XLOCKED); + + if (targp->p_pgrp == NULL || targp->p_session != curp->p_session) + return (EPERM); + if (SESS_LEADER(targp)) + return (EPERM); + if (pgid == 0) + pgid = targp->p_pid; + if ((pgrp = pgfind(pgid)) == NULL) { + if (pgid == targp->p_pid) { + error = enterpgrp(targp, pgid, *newpgrpp, NULL); + if (error == 0) + *newpgrpp = NULL; + } else + error = EPERM; + } else { + if (pgrp == targp->p_pgrp) { + PGRP_UNLOCK(pgrp); + return (0); + } + if (pgrp->pg_id != targp->p_pid && + pgrp->pg_session != curp->p_session) { + PGRP_UNLOCK(pgrp); + return (EPERM); + } + PGRP_UNLOCK(pgrp); + error = enterthispgrp(targp, pgrp); + } + return (error); +} + /* ARGSUSED */ int sys_setpgid(struct thread *td, struct setpgid_args *uap) { struct proc *curp = td->td_proc; struct proc *targp; /* target process */ - struct pgrp *pgrp; /* target pgrp */ int error; struct pgrp *newpgrp; @@ -474,12 +524,6 @@ PROC_UNLOCK(targp); goto done; } - if (targp->p_pgrp == NULL || - targp->p_session != curp->p_session) { - PROC_UNLOCK(targp); - error = EPERM; - goto done; - } if (targp->p_flag & P_EXEC) { PROC_UNLOCK(targp); error = EACCES; @@ -488,34 +532,7 @@ PROC_UNLOCK(targp); } else targp = curp; - if (SESS_LEADER(targp)) { - error = EPERM; - goto done; - } - if (uap->pgid == 0) - uap->pgid = targp->p_pid; - if ((pgrp = pgfind(uap->pgid)) == NULL) { - if (uap->pgid == targp->p_pid) { - error = enterpgrp(targp, uap->pgid, newpgrp, - NULL); - if (error == 0) - newpgrp = NULL; - } else - error = EPERM; - } else { - if (pgrp == targp->p_pgrp) { - PGRP_UNLOCK(pgrp); - goto done; - } - if (pgrp->pg_id != targp->p_pid && - pgrp->pg_session != curp->p_session) { - PGRP_UNLOCK(pgrp); - error = EPERM; - goto done; - } - PGRP_UNLOCK(pgrp); - error = enterthispgrp(targp, pgrp); - } + error = do_setpgid(curp, targp, uap->pgid, &newpgrp); done: KASSERT(error == 0 || newpgrp != NULL, ("setpgid failed and newpgrp is NULL")); diff --git a/sys/kern/subr_firmware.c b/sys/kern/subr_firmware.c --- a/sys/kern/subr_firmware.c +++ b/sys/kern/subr_firmware.c @@ -478,7 +478,7 @@ set_rootvnode(void *arg, int npending) { - pwd_ensure_dirs(); + pwd_ensure_dirs(curproc); free(arg, M_TEMP); } diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -885,18 +885,22 @@ int fd; }; #endif -int -sys_fchdir(struct thread *td, struct fchdir_args *uap) + +/* + * Resolve a directory descriptor to a vnode to be made a process's working + * directory, returned unlocked with a reference held, crossing any mount + * point as fchdir(2) does. Split out of sys_fchdir(). + */ +static int +chdir_getvp(struct thread *td, int dirfd, struct vnode **vpp) { struct vnode *vp, *tdp; struct mount *mp; struct file *fp; - int error; uint8_t fdflags; + int error; - AUDIT_ARG_FD(uap->fd); - error = getvnode_path(td, uap->fd, &cap_fchdir_rights, &fdflags, - &fp); + error = getvnode_path(td, dirfd, &cap_fchdir_rights, &fdflags, &fp); if (error != 0) return (error); if ((fdflags & UF_RESOLVE_BENEATH) != 0) { @@ -909,7 +913,7 @@ vn_lock(vp, LK_SHARED | LK_RETRY); AUDIT_ARG_VNODE1(vp); error = change_dir(vp, td); - while (!error && (mp = vp->v_mountedhere) != NULL) { + while (error == 0 && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, 0)) continue; error = VFS_ROOT(mp, LK_SHARED, &tdp); @@ -924,7 +928,21 @@ return (error); } VOP_UNLOCK(vp); - pwd_chdir(td, vp); + *vpp = vp; + return (0); +} + +int +sys_fchdir(struct thread *td, struct fchdir_args *uap) +{ + struct vnode *vp; + int error; + + AUDIT_ARG_FD(uap->fd); + error = chdir_getvp(td, uap->fd, &vp); + if (error != 0) + return (error); + pwd_chdir(td->td_proc, vp); return (0); } @@ -960,7 +978,7 @@ } VOP_UNLOCK(nd.ni_vp); NDFREE_PNBUF(&nd); - pwd_chdir(td, nd.ni_vp); + pwd_chdir(td->td_proc, nd.ni_vp); return (0); } @@ -973,7 +991,7 @@ * Takes locked vnode, unlocks it before returning. */ static int -kern_chroot(struct thread *td, struct vnode *vp) +kern_chroot_validate(struct thread *td, struct vnode *vp) { struct proc *p; int error; @@ -1002,14 +1020,43 @@ goto e_vunlock; #endif VOP_UNLOCK(vp); - error = pwd_chroot(td, vp); - vrele(vp); - return (error); + return (0); e_vunlock: vput(vp); return (error); } +/* + * Resolve a directory descriptor to a vnode to be made a process's root, + * returned validated and unlocked with a reference held. The descriptor is a + * chroot(2) target and is checked as one. Split out of sys_fchroot(). + */ +static int +chroot_getvp(struct thread *td, int dirfd, struct vnode **vpp) +{ + struct file *fp; + struct vnode *vp; + uint8_t fdflags; + int error; + + error = getvnode_path(td, dirfd, &cap_fchroot_rights, &fdflags, &fp); + if (error != 0) + return (error); + if ((fdflags & UF_RESOLVE_BENEATH) != 0) { + fdrop(fp, td); + return (ENOTCAPABLE); + } + vp = fp->f_vnode; + vrefact(vp); + fdrop(fp, td); + vn_lock(vp, LK_SHARED | LK_RETRY); + error = kern_chroot_validate(td, vp); + if (error != 0) + return (error); + *vpp = vp; + return (0); +} + /* * Change notion of root (``/'') directory. */ @@ -1030,7 +1077,11 @@ if (error != 0) return (error); NDFREE_PNBUF(&nd); - error = kern_chroot(td, nd.ni_vp); + error = kern_chroot_validate(td, nd.ni_vp); + if (error != 0) + return (error); + error = pwd_chroot(td->td_proc, nd.ni_vp); + vrele(nd.ni_vp); return (error); } @@ -1046,22 +1097,13 @@ sys_fchroot(struct thread *td, struct fchroot_args *uap) { struct vnode *vp; - struct file *fp; int error; - uint8_t fdflags; - error = getvnode_path(td, uap->fd, &cap_fchroot_rights, &fdflags, &fp); + error = chroot_getvp(td, uap->fd, &vp); if (error != 0) return (error); - if ((fdflags & UF_RESOLVE_BENEATH) != 0) { - fdrop(fp, td); - return (ENOTCAPABLE); - } - vp = fp->f_vnode; - vrefact(vp); - fdrop(fp, td); - vn_lock(vp, LK_SHARED | LK_RETRY); - error = kern_chroot(td, vp); + error = pwd_chroot(td->td_proc, vp); + vrele(vp); return (error); } diff --git a/sys/security/mac_veriexec_parser/mac_veriexec_parser.c b/sys/security/mac_veriexec_parser/mac_veriexec_parser.c --- a/sys/security/mac_veriexec_parser/mac_veriexec_parser.c +++ b/sys/security/mac_veriexec_parser/mac_veriexec_parser.c @@ -237,7 +237,7 @@ flags = FREAD; - pwd_ensure_dirs(); + pwd_ensure_dirs(curproc); NDINIT(nid, LOOKUP, 0, UIO_SYSSPACE, path); rc = vn_open(nid, &flags, 0, NULL); diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -370,10 +370,10 @@ void pdunshare(struct thread *td); void pwd_altroot(struct thread *td, struct vnode *altroot_vp); -void pwd_chdir(struct thread *td, struct vnode *vp); -int pwd_chroot(struct thread *td, struct vnode *vp); +void pwd_chdir(struct proc *p, struct vnode *vp); +int pwd_chroot(struct proc *p, struct vnode *vp); int pwd_chroot_chdir(struct thread *td, struct vnode *vp); -void pwd_ensure_dirs(void); +void pwd_ensure_dirs(struct proc *p); void pwd_set_rootvnode(void); struct pwd *pwd_hold_pwddesc(struct pwddesc *pdp); diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -3542,7 +3542,7 @@ break; } VOP_UNLOCK(vp); - pwd_chdir(td, vp); + pwd_chdir(td->td_proc, vp); break; case FFS_SET_DOTDOT: