Index: lib/libc/sys/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -420,6 +420,7 @@ FBSD_1.7 { _Fork; fspacectl; + pdopen; }; FBSDprivate_1.0 { Index: sys/ddb/db_ps.c =================================================================== --- sys/ddb/db_ps.c +++ sys/ddb/db_ps.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -494,6 +495,10 @@ } db_printf(" reaper: %p reapsubtree: %d\n", p->p_reaper, p->p_reapsubtree); + db_printf(" procdesc: %p\n", + (p->p_procdesc != NULL) ? p->p_procdesc : 0); + db_printf(" (refcount %d) \n", + (p->p_procdesc != NULL) ? p->p_procdesc->pd_refcount : 0); db_printf(" sigparent: %d\n", p->p_sigparent); db_printf(" vmspace: %p\n", p->p_vmspace); db_printf(" (map %p)\n", Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c +++ sys/kern/kern_exit.c @@ -1019,7 +1019,7 @@ switch (idtype) { case P_ALL: - if (p->p_procdesc == NULL || + if ((p->p_flag2 & P2_PDFORK) == 0 || (p->p_pptr == td->td_proc && (p->p_flag & P_TRACED) != 0)) { break; Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c +++ sys/kern/kern_fork.c @@ -684,8 +684,10 @@ * can happen that might cause that process to need the descriptor. * However, don't do this until after fork(2) can no longer fail. */ - if (fr->fr_flags & RFPROCDESC) + if (fr->fr_flags & RFPROCDESC) { procdesc_new(p2, fr->fr_pd_flags); + p2->p_flag2 |= P2_PDFORK; + } /* * 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 @@ -1598,6 +1598,13 @@ return (cr_cansignal(td->td_ucred, p, signum)); } +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 @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2009, 2016 Robert N. M. Watson + * Copyright (c) 2019, 2021 Mariusz Zaborksi * All rights reserved. * * This software was developed at the University of Cambridge Computer @@ -57,11 +58,6 @@ * using its process descriptor. * - The pdwait4(2) system call may be used to block (or not) on a process * descriptor to collect termination information. - * - * Open questions: - * - * - Will we want to add a pidtoprocdesc(2) system call to allow process - * descriptors to be created for processes without pdfork(2)? */ #include @@ -116,6 +112,45 @@ .fo_flags = DFLAG_PASSABLE, }; +static void procdesc_close_pd(struct procdesc *pd); + +static int +procdesc_alloc(int flags, bool waitok, struct procdesc **ppd) +{ + struct procdesc *pd; + + pd = malloc(sizeof(*pd), M_PROCDESC, M_ZERO | + (waitok ? M_WAITOK : M_NOWAIT)); + if (pd == NULL) + return (ENOMEM); + + pd->pd_flags = 0; + if (flags & PD_DAEMON) + pd->pd_flags |= PDF_DAEMON; + + *ppd = pd; + + return (0); +} + +static void +procdesc_install(struct proc *p, struct procdesc *pd) +{ + + pd->pd_proc = p; + pd->pd_pid = p->p_pid; + p->p_procdesc = pd; + PROCDESC_LOCK_INIT(pd); + knlist_init_mtx(&pd->pd_selinfo.si_note, &pd->pd_lock); + + /* + * 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); +} + + /* * Return a locked process given a process descriptor, or ESRCH if it has * died. @@ -164,6 +199,119 @@ return (pd->pd_pid); } +int +sys_pdopen(struct thread *td, struct pdopen_args *uap) +{ + int flag; + + flag = uap->flag; + if ((flag & ~PD_ALLOWED_AT_PDGETPID) != 0) + return (EINVAL); + + return (kern_pdopen(td, uap->pid, flag)); +} + +static int +pdopen_find_proc(struct thread *td, pid_t pid, struct proc **pproc) +{ + struct proc *proc; + int error; + + proc = pfind_any(pid); + if (proc == NULL) + return (ESRCH); + error = p_cangetpd(td, proc); + if (error != 0) { + PROC_UNLOCK(proc); + return (error); + } + + *pproc = proc; + + return (0); +} + +int +kern_pdopen(struct thread *td, pid_t pid, int flag) +{ + struct proc *proc; + struct procdesc *procdesc; + struct file *fp; + int fd, error; + + sx_slock(&proctree_lock); + error = pdopen_find_proc(td, pid, &proc); + if (error != 0) { + return (error); + } + + procdesc = NULL; + if (proc->p_procdesc == NULL) { + /* + * Process descriptor does not exists. + * If there is no memory we have to release lock and retry + * allocation. Then we will fetch a new process structure + * (it may be a different process with same pid), or the + * procdesc might be created by different process in the + * mean time. + */ + error = procdesc_alloc(0, false, &procdesc); + if (error != 0) { + PROC_UNLOCK(proc); + sx_sunlock(&proctree_lock); + + (void)procdesc_alloc(0, true, &procdesc); + + sx_slock(&proctree_lock); + error = pdopen_find_proc(td, pid, &proc); + if (error != 0) { + free(procdesc, M_PROCDESC); + return (error); + } + } + + if (proc->p_procdesc != NULL) { + free(procdesc, M_PROCDESC); + procdesc = NULL; + } else if ((proc->p_flag & P_WEXIT) == 0 && + proc->p_state != PRS_ZOMBIE) { + procdesc_install(proc, procdesc); + } else { + PROC_UNLOCK(proc); + sx_sunlock(&proctree_lock); + return (ECHILD); + } + } + + /* + * We didn't create a new process descriptor. + * So let's just increase the refcount. + */ + if (procdesc == NULL) { + procdesc = proc->p_procdesc; + refcount_acquire(&proc->p_procdesc->pd_refcount); + + PROCDESC_LOCK(procdesc); + procdesc->pd_flags &= ~PDF_CLOSED; + PROCDESC_UNLOCK(procdesc); + } + + PROC_UNLOCK(proc); + sx_sunlock(&proctree_lock); + + error = procdesc_falloc(td, &fp, &fd, flag, NULL); + if (error != 0) { + procdesc_close_pd(procdesc); + return (error); + } + + procdesc_finit(procdesc, fp); + fdrop(fp, td); + + td->td_retval[0] = fd; + return (0); +} + /* * Retrieve the PID associated with a process descriptor. */ @@ -213,21 +361,8 @@ { struct procdesc *pd; - pd = malloc(sizeof(*pd), M_PROCDESC, M_WAITOK | M_ZERO); - pd->pd_proc = p; - pd->pd_pid = p->p_pid; - p->p_procdesc = pd; - pd->pd_flags = 0; - if (flags & PD_DAEMON) - pd->pd_flags |= PDF_DAEMON; - PROCDESC_LOCK_INIT(pd); - knlist_init_mtx(&pd->pd_selinfo.si_note, &pd->pd_lock); - - /* - * 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); + (void)procdesc_alloc(flags, true, &pd); + procdesc_install(p, pd); } /* @@ -269,7 +404,7 @@ if (refcount_release(&pd->pd_refcount)) { KASSERT(pd->pd_proc == NULL, ("procdesc_free: pd_proc != NULL")); - KASSERT((pd->pd_flags & PDF_CLOSED), + KASSERT((pd->pd_flags & PDF_CLOSED) != 0, ("procdesc_free: !PDF_CLOSED")); knlist_destroy(&pd->pd_selinfo.si_note); @@ -295,8 +430,6 @@ pd = p->p_procdesc; PROCDESC_LOCK(pd); - KASSERT((pd->pd_flags & PDF_CLOSED) == 0 || p->p_pptr == p->p_reaper, - ("procdesc_exit: closed && parent not reaper")); pd->pd_flags |= PDF_EXITED; pd->pd_xstat = KW_EXITCODE(p->p_xexit, p->p_xsig); @@ -342,37 +475,43 @@ } /* - * procdesc_close() - last close on a process descriptor. If the process is - * still running, terminate with SIGKILL (unless PDF_DAEMON is set) and let - * its reaper clean up the mess; if not, we have to clean up the zombie - * ourselves. + * procdesc_close_pd() - close a process descriptor in single filedesc. + * When we close the last process descriptor in all filedesc then: + * If the process was created by the pdfork, last closed process descriptor + * means that there is no more handleres to process. We want to SIGKILL the + * process (unless PDF_DAEMON is set) and let its reaper clean up the mess. + * If the PDF_DAEMON is set we want to reparent it to the init process and allow + * it leave his own life. In case when the PDF_DAEMON flag is set or the pd was + * created because of the pdopen we keep the procdesc - some other process may + * obtain it. The procdesc finnaly will be clean by the exit1 function. + * If the process is already zombie and we close a last process descriptor, + * we have to clean up it ourselves. */ -static int -procdesc_close(struct file *fp, struct thread *td) +static void +procdesc_close_pd(struct procdesc *pd) { - struct procdesc *pd; struct proc *p; - KASSERT(fp->f_type == DTYPE_PROCDESC, ("procdesc_close: !procdesc")); - - pd = fp->f_data; - fp->f_ops = &badfileops; - 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) { /* * This is the case where process' exit status was already * collected and procdesc_reap() was already called. */ + procdesc_free(pd); sx_xunlock(&proctree_lock); - } else { - PROC_LOCK(p); - AUDIT_ARG_PROCESS(p); + return; + } + + PROC_LOCK(p); + AUDIT_ARG_PROCESS(p); + + if (refcount_load(&pd->pd_refcount) <= 2) { + PROCDESC_LOCK(pd); + pd->pd_flags |= PDF_CLOSED; + PROCDESC_UNLOCK(pd); + if (p->p_state == PRS_ZOMBIE) { /* * If the process is already dead and just awaiting @@ -381,23 +520,10 @@ * 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. - */ + return; + } else if ((p->p_flag2 & P2_PDFORK) != 0) { + p->p_flag2 &= ~P2_PDFORK; p->p_sigparent = SIGCHLD; if ((p->p_flag & P_TRACED) == 0) { proc_reparent(p, p->p_reaper, true); @@ -406,17 +532,35 @@ p->p_oppid = p->p_reaper->p_pid; proc_add_orphan(p, p->p_reaper); } - if ((pd->pd_flags & PDF_DAEMON) == 0) + + if ((pd->pd_flags & PDF_DAEMON) == 0) { + pd->pd_proc = NULL; + p->p_procdesc = NULL; + procdesc_free(pd); kern_psignal(p, SIGKILL); - PROC_UNLOCK(p); - sx_xunlock(&proctree_lock); + } } } - /* - * Release the file descriptor's reference on the process descriptor. - */ procdesc_free(pd); + + PROC_UNLOCK(p); + sx_xunlock(&proctree_lock); +} + +static int +procdesc_close(struct file *fp, struct thread *td) +{ + struct procdesc *pd; + + KASSERT(fp->f_type == DTYPE_PROCDESC, ("procdesc_close: !procdesc")); + + pd = fp->f_data; + fp->f_ops = &badfileops; + fp->f_data = NULL; + + procdesc_close_pd(pd); + return (0); } Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ sys/kern/syscalls.master @@ -3259,6 +3259,12 @@ _Out_opt_ struct spacectl_range *rmsr, ); } +581 AUE_NULL STD { + int pdopen( + pid_t pid, + int flag + ); + } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -838,6 +838,8 @@ #define P2_ITSTOPPED 0x00002000 #define P2_PTRACEREQ 0x00004000 /* Active ptrace req */ #define P2_NO_NEW_PRIVS 0x00008000 /* Ignore setuid */ +#define P2_PDFORK 0x00010000 /* Process is pdforked and + * parent is there */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ @@ -1117,6 +1119,7 @@ void maybe_yield(void); void mi_switch(int flags); 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,6 +97,7 @@ */ int procdesc_exit(struct proc *); int procdesc_find(struct thread *, int fd, cap_rights_t *, struct proc **); +int kern_pdopen(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); void procdesc_finit(struct procdesc *, struct file *); @@ -123,6 +124,7 @@ __BEGIN_DECLS pid_t pdfork(int *, int); int pdkill(int, int); +int pdopen(pid_t, int); int pdgetpid(int, pid_t *); __END_DECLS @@ -135,5 +137,6 @@ #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) #endif /* !_SYS_PROCDESC_H_ */ Index: tests/sys/kern/Makefile =================================================================== --- tests/sys/kern/Makefile +++ tests/sys/kern/Makefile @@ -12,6 +12,7 @@ ATF_TESTS_C+= kern_descrip_test ATF_TESTS_C+= fdgrowtable_test ATF_TESTS_C+= kill_zombie +ATF_TESTS_C+= pdopen_test ATF_TESTS_C+= ptrace_test TEST_METADATA.ptrace_test+= timeout="15" ATF_TESTS_C+= reaper Index: tests/sys/kern/pdopen_test.c =================================================================== --- /dev/null +++ tests/sys/kern/pdopen_test.c @@ -0,0 +1,774 @@ +/*- + * Copyright (c) 2021 Mariusz Zaborski + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +static int +verify_pdopen(pid_t pid, int flags) +{ + int fd; + pid_t gpid; + + fd = pdopen(pid, 0); + ATF_REQUIRE(fd >= 0); + ATF_REQUIRE(pdgetpid(fd, &gpid) == 0); + ATF_REQUIRE(gpid == pid); + + return (fd); +} + +static bool +test_signal(pid_t pid) +{ + int ret; + + if (kill(pid, SIGSTOP) != 0) + return (false); + if (waitpid(pid, &ret, WUNTRACED) != pid) + return (false); + if (WSTOPSIG(ret) != SIGSTOP) + return (false); + if (kill(pid, SIGCONT) != 0) + return (false); + + return (true); +} + +static bool +test_signal_pd(int fd, pid_t pid) +{ + int ret; + + if (pdkill(fd, SIGSTOP) != 0) + return (false); + if (waitpid(pid, &ret, WUNTRACED) != pid) + return (false); + if (WSTOPSIG(ret) != SIGSTOP) + return (false); + + if (pdkill(fd, SIGCONT) != 0) + return (false); + + return (true); +} + +static void +verify_alive(pid_t pid) +{ + + ATF_REQUIRE(kill(pid, 0) == 0); +} + +static void +verify_death(pid_t pid) +{ + pid_t wpid; + int status; + + wpid = waitpid(pid, &status, 0); + ATF_REQUIRE_EQ(wpid, -1); + ATF_REQUIRE_EQ(errno, ECHILD); + + if (kill(pid, 0) == 0) { + /* Give them a change to die. */ + sleep(1); + ATF_REQUIRE(kill(pid, 0) != 0); + } + ATF_REQUIRE_EQ(errno, ESRCH); +} + +static void +collect_process(pid_t pid) { + int status; + pid_t wpid; + + wpid = waitpid(pid, &status, 0); + ATF_REQUIRE_EQ(wpid, pid); + verify_death(pid); +} + +static void +clean_fork(pid_t pid) +{ + + ATF_REQUIRE_EQ(kill(pid, SIGTERM), 0); + collect_process(pid); +} + +static void +clean_fork_repranted(pid_t pid) +{ + + ATF_REQUIRE_EQ(kill(pid, SIGTERM), 0); + if (kill(pid, 0) == 0) { + /* Give them a change to die. */ + sleep(1); + ATF_REQUIRE(kill(pid, 0) != 0); + } + ATF_REQUIRE_EQ(errno, ESRCH); +} + +ATF_TC_WITHOUT_HEAD(pdopen__fork); +ATF_TC_BODY(pdopen__fork, tc) +{ + pid_t pid; + int fd; + + pid = fork(); + ATF_REQUIRE(pid != -1); + + if (pid == 0) { + pause(); + exit(0); + } + + fd = verify_pdopen(pid, 0); + + ATF_REQUIRE_EQ(test_signal(pid), true); + ATF_REQUIRE_EQ(test_signal_pd(fd, pid), true); + + close(fd); + ATF_REQUIRE_EQ(test_signal(pid), true); + + clean_fork(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__multiple_open_fork); +ATF_TC_BODY(pdopen__multiple_open_fork, tc) +{ + pid_t pid; + int i, fd[10]; + + pid = fork(); + ATF_REQUIRE(pid != -1); + + if (pid == 0) { + pause(); + exit(0); + } + + for (i = 0; i < nitems(fd); i++) { + fd[i] = verify_pdopen(pid, 0); + } + + ATF_REQUIRE_EQ(test_signal(pid), true); + + for (i = 0; i < nitems(fd); i++) { + ATF_REQUIRE_EQ(test_signal_pd(fd[i], pid), true); + } + + for (i = 0; i < nitems(fd); i++) { + close(fd[i]); + } + + ATF_REQUIRE_EQ(test_signal(pid), true); + + clean_fork(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__pdfork); +ATF_TC_BODY(pdopen__pdfork, tc) +{ + pid_t pid; + int fd, forkfd; + + pid = pdfork(&forkfd, 0); + ATF_REQUIRE(pid != -1); + + if (pid == 0) { + pause(); + exit(0); + } + + ATF_REQUIRE(forkfd >= 0); + + fd = verify_pdopen(pid, 0); + + ATF_REQUIRE_EQ(test_signal(pid), true); + ATF_REQUIRE_EQ(test_signal_pd(fd, pid), true); + ATF_REQUIRE_EQ(test_signal_pd(forkfd, pid), true); + + close(fd); + close(forkfd); + + /* + * Process created by pdfork. + * When last pd closed - process should die. + */ + verify_death(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__multiple_open_pdfork); +ATF_TC_BODY(pdopen__multiple_open_pdfork, tc) +{ + pid_t pid; + int i, fd[10], pdf; + + pid = pdfork(&pdf, 0); + ATF_REQUIRE(pid != -1); + + if (pid == 0) { + pause(); + exit(0); + } + + for (i = 0; i < nitems(fd); i++) { + fd[i] = verify_pdopen(pid, 0); + } + + ATF_REQUIRE_EQ(test_signal(pid), true); + + for (i = 0; i < nitems(fd); i++) { + ATF_REQUIRE_EQ(test_signal_pd(fd[i], pid), true); + } + + for (i = 0; i < nitems(fd); i++) { + close(fd[i]); + } + + ATF_REQUIRE_EQ(test_signal(pid), true); + + close(pdf); + verify_death(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__pdfork_close_pdopen); +ATF_TC_BODY(pdopen__pdfork_close_pdopen, tc) +{ + pid_t pid; + int fd, forkfd; + + pid = pdfork(&forkfd, 0); + ATF_REQUIRE(pid != -1); + + if (pid == 0) { + pause(); + exit(0); + } + + ATF_REQUIRE(forkfd >= 0); + + fd = verify_pdopen(pid, 0); + + ATF_REQUIRE_EQ(test_signal(pid), true); + ATF_REQUIRE_EQ(test_signal_pd(fd, pid), true); + ATF_REQUIRE_EQ(test_signal_pd(forkfd, pid), true); + + close(forkfd); + + ATF_REQUIRE_EQ(test_signal(pid), true); + ATF_REQUIRE_EQ(test_signal_pd(fd, pid), true); + + /* + * Process created by pdfork. + * When last pd closed - process should die. + */ + close(fd); + verify_death(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__pdfork_close_pdfork); +ATF_TC_BODY(pdopen__pdfork_close_pdfork, tc) +{ + pid_t pid; + int fd, forkfd; + + pid = pdfork(&forkfd, 0); + ATF_REQUIRE(pid != -1); + + if (pid == 0) { + pause(); + exit(0); + } + + ATF_REQUIRE(forkfd >= 0); + + fd = verify_pdopen(pid, 0); + + ATF_REQUIRE_EQ(test_signal(pid), true); + ATF_REQUIRE_EQ(test_signal_pd(fd, pid), true); + ATF_REQUIRE_EQ(test_signal_pd(forkfd, pid), true); + + close(fd); + + ATF_REQUIRE_EQ(test_signal(pid), true); + + /* + * Process created by pdfork. + * When last pd closed - process should die. + */ + close(forkfd); + verify_death(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__sibling_fork); +ATF_TC_BODY(pdopen__sibling_fork, tc) +{ + pid_t pid, sibling; + int fd; + + pid = fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + pause(); + exit(0); + } + + sibling = fork(); + ATF_REQUIRE(sibling != -1); + if (sibling == 0) { + fd = verify_pdopen(pid, 0); + + ATF_REQUIRE_EQ(pdkill(fd, SIGSTOP), 0); + + exit(0); + } + + collect_process(sibling); + + ATF_REQUIRE_EQ(test_signal(pid), true); + clean_fork(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__sibling_pdfork); +ATF_TC_BODY(pdopen__sibling_pdfork, tc) +{ + pid_t pid, sibling; + int fd; + + pid = pdfork(&fd, 0); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + pause(); + exit(0); + } + + sibling = fork(); + ATF_REQUIRE(sibling != -1); + if (sibling == 0) { + fd = verify_pdopen(pid, 0); + + ATF_REQUIRE_EQ(pdkill(fd, SIGSTOP), 0); + + exit(0); + } + + collect_process(sibling); + ATF_REQUIRE_EQ(test_signal(pid), true); + + close(fd); + verify_death(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__after_reparent_fork); +ATF_TC_BODY(pdopen__after_reparent_fork, tc) +{ + pid_t pid, reparent; + int fd, pip[2]; + + ATF_REQUIRE_EQ(pipe(pip), 0); + + pid = fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + close(pip[0]); + + reparent = fork(); + ATF_REQUIRE(reparent != -1); + if (reparent == 0) { + pause(); + exit(0); + } + + write(pip[1], &reparent, sizeof(reparent)); + + exit(0); + } + close(pip[1]); + + read(pip[0], &reparent, sizeof(reparent)); + + verify_alive(reparent); + + fd = verify_pdopen(reparent, 0); + close(fd); + + verify_alive(reparent); + clean_fork_repranted(reparent); +} + +ATF_TC_WITHOUT_HEAD(pdopen__before_reparent_fork); +ATF_TC_BODY(pdopen__before_reparent_fork, tc) +{ + pid_t pid, reparent; + int fd, pip[2]; + + ATF_REQUIRE_EQ(pipe(pip), 0); + + pid = fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + close(pip[0]); + + reparent = fork(); + ATF_REQUIRE(reparent != -1); + if (reparent == 0) { + pause(); + exit(0); + } + + write(pip[1], &reparent, sizeof(reparent)); + read(pip[1], &reparent, 1); + + exit(0); + } + close(pip[1]); + + read(pip[0], &reparent, sizeof(reparent)); + fd = verify_pdopen(reparent, 0); + write(pip[0], &(uint8_t){ 0 }, 1); + + collect_process(pid); + + close(fd); + verify_alive(reparent); + clean_fork_repranted(reparent); +} + +ATF_TC_WITHOUT_HEAD(pdopen__reparent_pdfork); +ATF_TC_BODY(pdopen__reparent_pdfork, tc) +{ + pid_t pid, reparent, parent; + int fd, pip[2], reparentpip[2], pfd, buf; + + ATF_REQUIRE_EQ(pipe(pip), 0); + ATF_REQUIRE_EQ(pipe(reparentpip), 0); + + buf = 0; + pid = fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + close(pip[0]); + close(reparentpip[0]); + + reparent = pdfork(&pfd, 0); + ATF_REQUIRE(reparent != -1); + if (reparent == 0) { + read(pip[1], &buf, 1); + parent = getppid(); + /* Give him a change to reparent. */ + if (parent != 1) { + sleep(1); + parent = getppid(); + } + write(reparentpip[1], &parent, sizeof(parent)); + pause(); + exit(0); + } + + write(pip[1], &reparent, sizeof(reparent)); + read(pip[1], &buf, 1); + close(pfd); + write(pip[1], &buf, sizeof(buf)); + + exit(0); + } + close(pip[1]); + close(reparentpip[1]); + + read(pip[0], &reparent, sizeof(reparent)); + fd = verify_pdopen(reparent, 0); + write(pip[0], &buf, sizeof(buf)); + read(pip[0], &buf, sizeof(buf)); + + collect_process(pid); + + read(reparentpip[0], &parent, sizeof(parent)); + ATF_REQUIRE_EQ(parent, 1); + ATF_REQUIRE_EQ(pdkill(fd, 0), 0); + + verify_alive(reparent); + close(fd); + verify_death(reparent); +} + +ATF_TC_WITHOUT_HEAD(pdopen__pdfork_daemon_before_reparent); +ATF_TC_BODY(pdopen__pdfork_daemon_before_reparent, tc) +{ + pid_t pid; + int fd, pfd; + + pid = pdfork(&pfd, PD_DAEMON); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + pause(); + exit(0); + } + + fd = verify_pdopen(pid, 0); + ATF_REQUIRE_EQ(test_signal_pd(fd, pid), true); + + close(fd); + close(pfd); + + verify_alive(pid); + + clean_fork_repranted(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__pdfork_daemon_after_reparent); +ATF_TC_BODY(pdopen__pdfork_daemon_after_reparent, tc) +{ + pid_t pid; + int fd, pip[2], pfd, buf; + + ATF_REQUIRE_EQ(pipe(pip), 0); + + buf = 0; + pid = pdfork(&pfd, PD_DAEMON); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + close(pip[0]); + read(pip[1], &buf, 1); + ATF_REQUIRE_EQ(getppid(), 1); + pause(); + exit(0); + } + close(pip[1]); + close(pfd); + write(pip[0], &buf, 1); + + verify_alive(pid); + + fd = verify_pdopen(pid, 0); + close(fd); + + verify_alive(pid); + + clean_fork_repranted(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__pdfork_daemon_no_reparent_after_open); +ATF_TC_BODY(pdopen__pdfork_daemon_no_reparent_after_open, tc) +{ + pid_t pid, parent; + int fd, pip[2], pfd, buf; + + ATF_REQUIRE_EQ(pipe(pip), 0); + + buf = 0; + pid = pdfork(&pfd, PD_DAEMON); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + close(pip[0]); + read(pip[1], &buf, 1); + parent = getppid(); + write(pip[1], &parent, sizeof(parent)); + + read(pip[1], &buf, 1); + parent = getppid(); + write(pip[1], &parent, sizeof(parent)); + + pause(); + exit(0); + } + + close(pip[1]); + + /* No reparenitng after closing pfd. */ + fd = verify_pdopen(pid, 0); + close(pfd); + verify_alive(pid); + write(pip[0], &buf, 1); + read(pip[0], &parent, sizeof(parent)); + ATF_REQUIRE_EQ(parent, getpid()); + + /* Reparenting when there is no pd. */ + close(fd); + verify_alive(pid); + write(pip[0], &buf, 1); + read(pip[0], &parent, sizeof(parent)); + ATF_REQUIRE_EQ(parent, 1); + + clean_fork_repranted(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__pdfork_daemon_no_reparent_after_open_sibling); +ATF_TC_BODY(pdopen__pdfork_daemon_no_reparent_after_open_sibling, tc) +{ + pid_t pid, parent, sibling; + int fd, pip[2], pfd, buf, siblingpip[2], i; + + ATF_REQUIRE_EQ(pipe(pip), 0); + + buf = 0; + pid = pdfork(&pfd, PD_DAEMON); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + close(pip[0]); + read(pip[1], &buf, 1); + parent = getppid(); + write(pip[1], &parent, sizeof(parent)); + + read(pip[1], &buf, 1); + + /* Give him a change to reparent. */ + parent = getppid(); + if (parent != 1) { + sleep(1); + parent = getppid(); + } + write(pip[1], &parent, sizeof(parent)); + + pause(); + exit(0); + } + + close(pip[1]); + ATF_REQUIRE_EQ(pipe(siblingpip), 0); + + sibling = fork(); + ATF_REQUIRE(sibling != -1); + if (sibling == 0) { + close(pip[0]); + close(siblingpip[0]); + close(pfd); + + fd = verify_pdopen(pid, 0); + + write(siblingpip[1], &buf, sizeof(buf)); + read(siblingpip[1], &buf, sizeof(buf)); + + close(fd); + + write(siblingpip[1], &buf, sizeof(buf)); + + exit(0); + } + + read(siblingpip[0], &buf, sizeof(buf)); + close(pfd); + + /* Only sibling have a pfd - no reparenting. */ + verify_alive(pid); + write(pip[0], &buf, sizeof(buf)); + read(pip[0], &parent, sizeof(parent)); + ATF_REQUIRE_EQ(parent, getpid()); + + /* Close pfd in sibling - reparenting. */ + write(siblingpip[0], &buf, sizeof(buf)); + read(siblingpip[0], &buf, sizeof(buf)); + verify_alive(pid); + write(pip[0], &buf, sizeof(buf)); + read(pip[0], &parent, sizeof(parent)); + ATF_REQUIRE_EQ(parent, 1); + + clean_fork(sibling); + clean_fork_repranted(pid); +} + +ATF_TC_WITHOUT_HEAD(pdopen__pdopen_failed_fork); +ATF_TC_BODY(pdopen__pdopen_failed_fork, tc) +{ + struct rlimit limit; + pid_t pid, failed; + int fd; + + pid = fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + pause(); + exit(0); + } + + /* + * This should create a procdesc structure in kernel + * and allow us to reuse it later. + */ + failed = fork(); + ATF_REQUIRE(failed != -1); + if (failed == 0) { + limit.rlim_cur = 0; + limit.rlim_max = 0; + ATF_REQUIRE_EQ(setrlimit(RLIMIT_NOFILE, &limit), 0); + ATF_REQUIRE(pdopen(pid, 0) < 0); + ATF_REQUIRE_EQ(errno, EMFILE); + exit(0); + } + + collect_process(failed); + + fd = verify_pdopen(pid, 0); + + verify_alive(pid); + ATF_REQUIRE_EQ(test_signal(pid), true); + ATF_REQUIRE_EQ(test_signal_pd(fd, pid), true); + + close(fd); + + verify_alive(pid); + clean_fork(pid); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, pdopen__fork); + ATF_TP_ADD_TC(tp, pdopen__multiple_open_fork); + ATF_TP_ADD_TC(tp, pdopen__pdfork); + ATF_TP_ADD_TC(tp, pdopen__multiple_open_pdfork); + ATF_TP_ADD_TC(tp, pdopen__pdfork_close_pdopen); + ATF_TP_ADD_TC(tp, pdopen__pdfork_close_pdfork); + + ATF_TP_ADD_TC(tp, pdopen__sibling_fork); + ATF_TP_ADD_TC(tp, pdopen__sibling_pdfork); + + ATF_TP_ADD_TC(tp, pdopen__after_reparent_fork); + ATF_TP_ADD_TC(tp, pdopen__before_reparent_fork); + ATF_TP_ADD_TC(tp, pdopen__reparent_pdfork); + + ATF_TP_ADD_TC(tp, pdopen__pdfork_daemon_before_reparent); + ATF_TP_ADD_TC(tp, pdopen__pdfork_daemon_after_reparent); + ATF_TP_ADD_TC(tp, pdopen__pdfork_daemon_no_reparent_after_open); + ATF_TP_ADD_TC(tp, pdopen__pdfork_daemon_no_reparent_after_open_sibling); + + ATF_TP_ADD_TC(tp, pdopen__pdopen_failed_fork); + + return (atf_no_error()); +}