Index: lib/libc/sys/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -407,6 +407,7 @@ fhreadlink; getfhat; funlinkat; + pidgetpd; }; FBSDprivate_1.0 { Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c +++ sys/kern/kern_fork.c @@ -652,7 +652,7 @@ * However, don't do this until after fork(2) can no longer fail. */ if (fr->fr_flags & RFPROCDESC) - procdesc_new(p2, fr->fr_pd_flags); + procdesc_new(p2, fr->fr_pd_flags | PD_WAITOK); /* * Both processes are set up, now check if any loadable modules want Index: sys/kern/kern_prot.c =================================================================== --- sys/kern/kern_prot.c +++ sys/kern/kern_prot.c @@ -1593,6 +1593,21 @@ return (cr_cansignal(td->td_ucred, p, signum)); } +/*- + * Determine whether td may deliver the specified signal to p. + * Returns: 0 for permitted, an errno value otherwise + * Locks: Sufficient locks to protect various components of td and p + * must be held. td must be curthread, and a lock must be + * held for p. + * References: td and p must be valid for the lifetime of the call + */ +int +p_cangetpd(struct thread *td, struct proc *p) +{ + + return (p_cansignal(td, p, SIGKILL)); +} + /*- * Determine whether td may reschedule p. * Returns: 0 for permitted, an errno value otherwise Index: sys/kern/sys_procdesc.c =================================================================== --- sys/kern/sys_procdesc.c +++ sys/kern/sys_procdesc.c @@ -61,8 +61,6 @@ * Open questions: * * - How to handle ptrace(2)? - * - Will we want to add a pidtoprocdesc(2) system call to allow process - * descriptors to be created for processes without pdfork(2)? */ #include @@ -181,6 +179,72 @@ return (pd->pd_pid); } +int +sys_pidgetpd(struct thread *td, struct pidgetpd_args *uap) +{ + int flag; + + flag = uap->flag; + if ((flag & ~PD_ALLOWED_AT_PDGETPID) != 0) + return (EINVAL); + + return (kern_pidgetpd(td, uap->pid, uap->flag)); +} + +int +kern_pidgetpd(struct thread *td, pid_t pid, int flag) +{ + struct proc *proc; + struct procdesc *procdesc; + struct file *fp; + int fd, error; + + proc = pfind_any(pid); + if (proc == NULL) + return (ESRCH); + error = p_cangetpd(td, proc); + if (error != 0) { + PROC_UNLOCK(proc); + return (error); + } + if ((proc->p_flag2 & P2_PD_CLOSED) != 0) { + PROC_UNLOCK(proc); + return (ESRCH); + } + + if (proc->p_procdesc == NULL) { + /* + * We create a new process descriptors as DAEMON that + * it will not be killed when the pd will be closed. + */ + if (procdesc_new(proc, PD_DAEMON) != 0) { + PROC_UNLOCK(proc); + return (ENOMEM); + } + } + + procdesc = proc->p_procdesc; + refcount_acquire(&procdesc->pd_refcount); + PROC_UNLOCK(proc); + + error = procdesc_falloc(td, &fp, &fd, flag, NULL); + if (error == 0) { + /* + * If we alloc a new procdesc instance and we can't assign it + * we don't care about freeing procdesc. We can reuse it + * later. + */ + refcount_release(&procdesc->pd_refcount); + return (error); + } + + procdesc_finit(procdesc, fp); + fdrop(fp, td); + + td->td_retval[0] = fd; + return (0); +} + /* * Retrieve the PID associated with a process descriptor. */ @@ -225,12 +289,16 @@ * chance to set up the process descriptor. Failure is not permitted at this * point, so procdesc_new() must succeed. */ -void +int procdesc_new(struct proc *p, int flags) { struct procdesc *pd; - pd = uma_zalloc(procdesc_zone, M_WAITOK | M_ZERO); + pd = uma_zalloc(procdesc_zone, M_ZERO | + ((flags & PD_WAITOK) != 0 ? M_WAITOK : 0)); + if (pd == NULL) + return (-1); + pd->pd_proc = p; pd->pd_pid = p->p_pid; p->p_procdesc = pd; @@ -244,7 +312,9 @@ * Process descriptors start out with two references: one from their * struct file, and the other from their struct proc. */ - refcount_init(&pd->pd_refcount, 2); + refcount_init(&pd->pd_refcount, 1); + + return (0); } /* @@ -274,7 +344,7 @@ } static void -procdesc_free(struct procdesc *pd) +_procdesc_free(struct procdesc *pd) { /* @@ -283,16 +353,20 @@ * detach the descriptor before the process dies if the descript is * closed, as we can't wait synchronously. */ - if (refcount_release(&pd->pd_refcount)) { - KASSERT(pd->pd_proc == NULL, - ("procdesc_free: pd_proc != NULL")); - KASSERT((pd->pd_flags & PDF_CLOSED), - ("procdesc_free: !PDF_CLOSED")); - - knlist_destroy(&pd->pd_selinfo.si_note); - PROCDESC_LOCK_DESTROY(pd); - uma_zfree(procdesc_zone, pd); - } + KASSERT(pd->pd_proc == NULL, ("procdesc_free: pd_proc != NULL")); + KASSERT((pd->pd_flags & PDF_CLOSED), ("procdesc_free: !PDF_CLOSED")); + + knlist_destroy(&pd->pd_selinfo.si_note); + PROCDESC_LOCK_DESTROY(pd); + uma_zfree(procdesc_zone, pd); +} + +static void +procdesc_free(struct procdesc *pd) +{ + + if (refcount_release(&pd->pd_refcount)) + _procdesc_free(pd); } /* @@ -377,9 +451,6 @@ fp->f_data = NULL; sx_xlock(&proctree_lock); - PROCDESC_LOCK(pd); - pd->pd_flags |= PDF_CLOSED; - PROCDESC_UNLOCK(pd); p = pd->pd_proc; if (p == NULL) { /* @@ -387,47 +458,56 @@ * collected and procdesc_reap() was already called. */ sx_xunlock(&proctree_lock); - } else { - PROC_LOCK(p); - AUDIT_ARG_PROCESS(p); - if (p->p_state == PRS_ZOMBIE) { - /* - * If the process is already dead and just awaiting - * reaping, do that now. This will release the - * process's reference to the process descriptor when it - * calls back into procdesc_reap(). - */ - proc_reap(curthread, p, NULL, 0); - } else { - /* - * If the process is not yet dead, we need to kill it, - * but we can't wait around synchronously for it to go - * away, as that path leads to madness (and deadlocks). - * First, detach the process from its descriptor so that - * its exit status will be reported normally. - */ - pd->pd_proc = NULL; - p->p_procdesc = NULL; - procdesc_free(pd); - - /* - * Next, reparent it to its reaper (usually init(8)) so - * that there's someone to pick up the pieces; finally, - * terminate with prejudice. - */ - p->p_sigparent = SIGCHLD; - proc_reparent(p, p->p_reaper, true); - if ((pd->pd_flags & PDF_DAEMON) == 0) - kern_psignal(p, SIGKILL); - PROC_UNLOCK(p); - sx_xunlock(&proctree_lock); + procdesc_free(pd); + return (0); + } + + PROC_LOCK(p); + AUDIT_ARG_PROCESS(p); + if (p->p_state == PRS_ZOMBIE) { + /* + * If the process is already dead and just awaiting + * reaping, do that now. This will release the + * process's reference to the process descriptor when it + * calls back into procdesc_reap(). + */ + PROCDESC_LOCK(pd); + pd->pd_flags |= PDF_CLOSED; + PROCDESC_UNLOCK(pd); + proc_reap(curthread, p, NULL, 0); + return (0); + } else if (refcount_release(&pd->pd_refcount)) { + /* + * If the process is not yet dead, we need to kill it, + * but we can't wait around synchronously for it to go + * away, as that path leads to madness (and deadlocks). + * First, detach the process from its descriptor so that + * its exit status will be reported normally. + */ + PROCDESC_LOCK(pd); + pd->pd_flags |= PDF_CLOSED; + PROCDESC_UNLOCK(pd); + pd->pd_proc = NULL; + p->p_procdesc = NULL; + + /* + * Reparent it to init(8) so that there's someone + * to pick up the pieces; finally, terminate with + * prejudice. + */ + p->p_sigparent = SIGCHLD; + proc_reparent(p, initproc, true); + if ((pd->pd_flags & PDF_DAEMON) == 0) { + p->p_flag2 |= P2_PD_CLOSED; + kern_psignal(p, SIGKILL); } + + _procdesc_free(pd); } - /* - * Release the file descriptor's reference on the process descriptor. - */ - procdesc_free(pd); + PROC_UNLOCK(p); + sx_xunlock(&proctree_lock); + return (0); } Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -758,6 +758,7 @@ #define P2_ASLR_ENABLE 0x00000040 /* Force enable ASLR. */ #define P2_ASLR_DISABLE 0x00000080 /* Force disable ASLR. */ #define P2_ASLR_IGNSTART 0x00000100 /* Enable ASLR to consume sbrk area. */ +#define P2_PD_CLOSED 0x00000200 /* Enable ASLR to consume sbrk area. */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ @@ -1048,6 +1049,7 @@ void maybe_yield(void); void mi_switch(int flags, struct thread *newtd); int p_candebug(struct thread *td, struct proc *p); +int p_cangetpd(struct thread *td, struct proc *p); int p_cansee(struct thread *td, struct proc *p); int p_cansched(struct thread *td, struct proc *p); int p_cansignal(struct thread *td, struct proc *p, int signum); Index: sys/sys/procdesc.h =================================================================== --- sys/sys/procdesc.h +++ sys/sys/procdesc.h @@ -97,8 +97,9 @@ */ int procdesc_exit(struct proc *); int procdesc_find(struct thread *, int fd, cap_rights_t *, struct proc **); +int kern_pidgetpd(struct thread *, pid_t pid, int flag); int kern_pdgetpid(struct thread *, int fd, cap_rights_t *, pid_t *pidp); -void procdesc_new(struct proc *, int); +int procdesc_new(struct proc *, int); void procdesc_finit(struct procdesc *, struct file *); pid_t procdesc_pid(struct file *); void procdesc_reap(struct proc *); @@ -135,5 +136,8 @@ #define PD_CLOEXEC 0x00000002 /* Close file descriptor on exec. */ #define PD_ALLOWED_AT_FORK (PD_DAEMON | PD_CLOEXEC) +#define PD_ALLOWED_AT_PDGETPID (PD_CLOEXEC) + +#define PD_WAITOK 0x10000000 /* Allow to wait while creating. */ #endif /* !_SYS_PROCDESC_H_ */