Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F164013803
D58264.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D58264.diff
View Options
diff --git a/lib/libsys/pdfork.2 b/lib/libsys/pdfork.2
--- a/lib/libsys/pdfork.2
+++ b/lib/libsys/pdfork.2
@@ -103,6 +103,9 @@
flag, closing that descriptor kills the process.
.It Dv PD_CLOEXEC
Set close-on-exec on process descriptor.
+.It Dv PD_NOWAITPID
+The parent does not get the child status with
+.Xr waitpid 2 .
.El
.Pp
The
@@ -176,7 +179,7 @@
.Fa fd
process descriptor.
See the description of the
-.Xr wait6
+.Xr wait6 2
system call for the behavior specification.
.Pp
The
@@ -197,6 +200,50 @@
argument is reserved and must be zero.
Certain file descriptor types cannot be copied this way, namely
kqueues.
+.Sh INTERACTION OF PROCESS DESCRIPTORS AND Xr WAITPID 2
+.Pp
+The
+.Fn pdwait
+function may be called on any process descriptor, after the
+process exited, unlimited number of times.
+Each time, it returns the same status.
+.Bl -dash
+.It
+If the process was forked with
+.Fn pdfork ,
+and the
+.Dv PD_NOWAITPID
+flag was specified, then the process is reaped after the
+last process descriptor is closed.
+No
+.Xr waitpid 2
+call
+.Pq or a call from the same family of the wait functions working on PIDs
+are needed to reap the zombie process.
+.It
+If the process was forked with
+.Fn pdfork ,
+and the
+.Dv PD_NOWAITPID
+flag was not specified, then both
+.Xr waitpid 2
+call from the parent, and close of all process descriptors referencing
+the process, are required to reap the zombie process.
+.It
+If the process was created by the
+.Xr fork 2
+system call
+.Pq which does not allocates a process descriptor for the child ,
+and later the process was opened by
+.Fn pdopenpid ,
+then the
+.Xr waitpid 2
+call from the parent is needed to reap.
+.El
+.Pp
+In any case, the PID of the process is not reused until its zombie
+is reaped, and all its process descriptors are closed.
+.Sh INTERACTION OF PROCESS DESCRIPTORS WITH SOME SYSTEM CALLS
.Pp
The following system calls also have effects specific to process descriptors:
.Pp
diff --git a/lib/libsys/wait.2 b/lib/libsys/wait.2
--- a/lib/libsys/wait.2
+++ b/lib/libsys/wait.2
@@ -607,15 +607,6 @@
.Fa infop
must be checked against zero to determine if a process reported status.
.Pp
-The
-.Fn wait
-family of functions will only return a child process created with
-.Xr pdfork 2
-if the calling process is not in
-.Xr capsicum 4
-capability mode, and
-.Nm
-has been explicitly given the child's process ID.
.Sh ERRORS
The
.Fn wait
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -124,6 +124,9 @@
}
parent = __containerof(p->p_orphan.le_prev, struct proc,
p_orphans.lh_first);
+ KASSERT(child->p_pptr != parent,
+ ("proc %d %p orphaned but parent %d %p is realparent",
+ child->p_pid, child, parent->p_pid, parent));
return (parent);
}
@@ -681,7 +684,7 @@
*/
signal_parent = 0;
procdesc_exit(p);
- if (p->p_procdesc == NULL) {
+ if (p->p_procdesc == NULL || procdesc_exit(p)) {
/*
* Notify parent that we're gone. If parent has the
* PS_NOCLDWAIT flag set, or if the handler is set to SIG_IGN,
@@ -983,7 +986,8 @@
* lock as part of its work.
*/
void
-proc_reap(struct thread *td, struct proc *p, int *status, int options)
+proc_reap(struct thread *td, struct proc *p, int *status, int options,
+ int zombieref)
{
struct proc *q, *t;
@@ -1007,6 +1011,13 @@
return;
}
+ p->p_zombieref &= ~zombieref;
+ if (p->p_zombieref != 0) {
+ PROC_UNLOCK(p);
+ sx_xunlock(&proctree_lock);
+ return;
+ }
+
PROC_LOCK(q);
sigqueue_take(p->p_ksi);
PROC_UNLOCK(q);
@@ -1181,7 +1192,7 @@
static int
proc_to_reap(struct thread *td, struct proc *p, idtype_t idtype, id_t id,
int *status, int options, struct __wrusage *wrusage, siginfo_t *siginfo,
- bool check_only)
+ int zombieref)
{
sx_assert(&proctree_lock, SA_XLOCKED);
@@ -1189,9 +1200,8 @@
switch (idtype) {
case P_ALL:
- if (p->p_procdesc == NULL ||
- (p->p_pptr == td->td_proc &&
- (p->p_flag & P_TRACED) != 0)) {
+ if ((p->p_zombieref & zombieref) != 0 ||
+ (p->p_pptr == td->td_proc && (p->p_flag & P_TRACED) != 0)) {
break;
}
@@ -1243,12 +1253,9 @@
return (0);
}
- if (p_canwait(td, p)) {
- PROC_UNLOCK(p);
- return (0);
- }
-
- if ((options & WEXITED) == 0 && p->p_state == PRS_ZOMBIE) {
+ if (p_canwait(td, p) != 0 ||
+ ((options & WEXITED) == 0 && p->p_state == PRS_ZOMBIE) ||
+ (p->p_zombieref & zombieref) == 0) {
PROC_UNLOCK(p);
return (0);
}
@@ -1276,8 +1283,8 @@
*/
wait_fill_wrusage(p, wrusage);
- if (p->p_state == PRS_ZOMBIE && !check_only) {
- proc_reap(td, p, status, options);
+ if (p->p_state == PRS_ZOMBIE) {
+ proc_reap(td, p, status, options, zombieref);
return (-1);
}
return (1);
@@ -1470,7 +1477,7 @@
LIST_FOREACH(p, &q->p_children, p_sibling) {
pid = p->p_pid;
ret = proc_to_reap(td, p, idtype, id, status, options,
- wrusage, siginfo, false);
+ wrusage, siginfo, PZOMBIEREF_PARENT);
if (ret == 0)
continue;
else if (ret != 1) {
@@ -1478,18 +1485,6 @@
return (0);
}
- /*
- * When running in capsicum(4) mode, make wait(2) ignore
- * processes created with pdfork(2). This is because one can
- * disown them - by passing their process descriptor to another
- * process - which means it needs to be prevented from touching
- * them afterwards.
- */
- if (IN_CAPABILITY_MODE(td) && p->p_procdesc != NULL) {
- PROC_UNLOCK(p);
- continue;
- }
-
nfound++;
PROC_LOCK_ASSERT(p, MA_OWNED);
@@ -1516,7 +1511,7 @@
if (nfound == 0) {
LIST_FOREACH(p, &q->p_orphans, p_orphan) {
ret = proc_to_reap(td, p, idtype, id, NULL, options,
- NULL, NULL, true);
+ NULL, NULL, 0);
if (ret != 0) {
KASSERT(ret != -1, ("reaped an orphan (pid %d)",
(int)td->td_retval[0]));
@@ -1602,10 +1597,6 @@
error = p_canwait(td, p);
if (error != 0)
break;
- if ((options & WEXITED) == 0 && p->p_state == PRS_ZOMBIE) {
- error = ESRCH;
- break;
- }
wait_fill_siginfo(p, siginfo);
wait_fill_wrusage(p, wrusage);
@@ -1679,6 +1670,14 @@
child->p_pptr = parent;
if (set_oppid)
child->p_oppid = parent->p_pid;
+
+ /*
+ * When reparenting the child to the reaper, re-enable
+ * waitpid(2) for it, so that the zombie can be collected.
+ */
+ if ((child->p_flag & P_TRACED) == 0 && child->p_reaper == parent &&
+ (child->p_zombieref & PZOMBIEREF_PARENT) == 0)
+ child->p_zombieref |= PZOMBIEREF_PARENT;
}
static void
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -547,6 +547,13 @@
P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC | P2_NO_NEW_PRIVS |
P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC | P2_LOGSIGEXIT_CTL |
P2_LOGSIGEXIT_ENABLE);
+ if ((fr->fr_flags & RFPROCDESC) != 0) {
+ p2->p_zombieref = PZOMBIEREF_PROCDESC;
+ if ((fr->fr_pd_flags & PD_NOWAITPID) == 0)
+ p2->p_zombieref |= PZOMBIEREF_PARENT;
+ } else {
+ p2->p_zombieref = PZOMBIEREF_PARENT;
+ }
p2->p_swtick = ticks;
if (p1->p_flag & P_PROFIL)
startprofclock(p2);
diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c
--- a/sys/kern/sys_procdesc.c
+++ b/sys/kern/sys_procdesc.c
@@ -278,7 +278,7 @@
* We use the proctree_lock to ensure that process exit either happens
* strictly before or strictly after a concurrent call to procdesc_close().
*/
-void
+bool
procdesc_exit(struct proc *p)
{
struct procdesc *pd;
@@ -289,7 +289,7 @@
pd = p->p_procdesc;
if (pd == NULL)
- return;
+ goto out;
PROCDESC_LOCK(pd);
KASSERT(pd->pd_fpcount > 0, ("%s: closed procdesc %p", __func__, pd));
@@ -306,6 +306,8 @@
/* Wakeup all waiters for this procdesc' process exit. */
wakeup(&p->p_procdesc);
+out:
+ return ((p->p_zombieref & PZOMBIEREF_PARENT) != 0);
}
void
@@ -400,15 +402,7 @@
} 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 (pd->pd_fpcount == 0) /* last procdesc */ {
+ if (pd->pd_fpcount == 0) /* last procdesc */ {
/*
* If the process is not yet dead, we need to kill it,
* but we can't wait around synchronously for it to go
@@ -420,9 +414,16 @@
p->p_procdesc = NULL;
pd->pd_pid = -1;
procdesc_free(pd);
-
- /* Failed finstall() should not cause reaping. */
- if ((fp->f_pdflags & F_PD_NOFINSTALL) == 0) {
+ if (p->p_state == PRS_ZOMBIE) {
+ proc_reap(curthread, p, NULL, 0,
+ PZOMBIEREF_PROCDESC);
+ goto out;
+ /*
+ * A reference for waitpid() or failed
+ * finstall() should not cause reaping.
+ */
+ } else if ((fp->f_pdflags & F_PD_NOFINSTALL) == 0 &&
+ (p->p_zombieref & PZOMBIEREF_PARENT) == 0) {
/*
* Next, reparent it to its reaper
* (usually init(8)) so that there's
@@ -443,7 +444,7 @@
procdesc_close_tail(fp, p);
}
}
-
+out:
/*
* Release the file descriptor's reference on the process descriptor.
*/
@@ -634,6 +635,7 @@
}
pd = p->p_procdesc;
if (pd != NULL) {
+ MPASS((p->p_zombieref & PZOMBIEREF_PROCDESC) != 0);
refcount_acquire(&pd->pd_refcount);
PROCDESC_LOCK(pd);
MPASS(pd->pd_fpcount > 0);
@@ -645,6 +647,8 @@
pd->pd_proc = p;
pd->pd_pid = p->p_pid;
p->p_procdesc = pd;
+ MPASS((p->p_zombieref & PZOMBIEREF_PROCDESC) == 0);
+ p->p_zombieref |= PZOMBIEREF_PROCDESC;
}
procdesc_finit(pd, fp);
PROC_UNLOCK(p);
@@ -700,7 +704,7 @@
AUDIT_ARG_PID(args->pid);
AUDIT_ARG_FFLAGS(args->flags);
- if ((args->flags & ~(PD_ALLOWED_AT_FORK)) != 0)
+ if ((args->flags & ~(PD_ALLOWED_AT_OPENPID)) != 0)
return (EINVAL);
return (kern_pdopenpid(td, args->pid, args->flags));
}
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -782,6 +782,7 @@
LIST_ENTRY(proc) p_jaillist; /* (d) Jail process linkage. */
u_int p_asig; /* (c) ASYNCEXIT pending signal. */
u_int p_tree_refcnt; /* (e) proctree refcount */
+ u_int p_zombieref; /* (e) References for reap. */
};
#define p_session p_pgrp->pg_session
@@ -911,6 +912,12 @@
#define P_TREE_REAPER 0x00000004 /* Reaper of subtree */
#define P_TREE_GRPEXITED 0x00000008 /* exit1() done with job ctl */
+/*
+ * p_zombieref; protected by proctree_lock.
+ */
+#define PZOMBIEREF_PARENT 0x00000001 /* Ref for waitpid() */
+#define PZOMBIEREF_PROCDESC 0x00000002 /* Ref for pdwait() */
+
/*
* These were process status values (p_stat), now they are only used in
* legacy conversion code.
@@ -1205,7 +1212,8 @@
void proc_linkup0(struct proc *p, struct thread *td);
void proc_linkup(struct proc *p, struct thread *td);
struct proc *proc_realparent(struct proc *child);
-void proc_reap(struct thread *td, struct proc *p, int *status, int options);
+void proc_reap(struct thread *td, struct proc *p, int *status, int options,
+ int zombieref);
void proc_reparent(struct proc *child, struct proc *newparent, bool set_oppid);
void proc_set_p2_wexit(struct proc *p);
void proc_set_traced(struct proc *p, bool stop);
diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h
--- a/sys/sys/procdesc.h
+++ b/sys/sys/procdesc.h
@@ -107,7 +107,7 @@
/*
* In-kernel interfaces to process descriptors.
*/
-void procdesc_exit(struct proc *);
+bool procdesc_exit(struct proc *);
void procdesc_fork(struct proc *p, pid_t child_pid);
void procdesc_jobstate(struct proc *p);
int kern_pdgetpid(struct thread *, int fd, const cap_rights_t *,
@@ -158,7 +158,9 @@
*/
#define PD_DAEMON 0x00000001 /* Don't exit when procdesc closes. */
#define PD_CLOEXEC 0x00000002 /* Close file descriptor on exec. */
+#define PD_NOWAITPID 0x00000004 /* Reap without waitpid(). */
-#define PD_ALLOWED_AT_FORK (PD_DAEMON | PD_CLOEXEC)
+#define PD_ALLOWED_AT_FORK (PD_DAEMON | PD_CLOEXEC | PD_NOWAITPID)
+#define PD_ALLOWED_AT_OPENPID (PD_DAEMON | PD_CLOEXEC)
#endif /* !_SYS_PROCDESC_H_ */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Jul 28, 10:10 PM (14 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35674793
Default Alt Text
D58264.diff (11 KB)
Attached To
Mode
D58264: processes: add zombie references, each of them prevents reap
Attached
Detach File
Event Timeline
Log In to Comment