svn commit: r287603 - in stable/9: bin/ps sys/kern sys/sys
John Baldwin
jhb at FreeBSD.org
Wed Sep 9 23:05:55 UTC 2015
Author: jhb
Date: Wed Sep 9 23:05:52 2015
New Revision: 287603
URL: https://svnweb.freebsd.org/changeset/base/287603
Log:
MFC 269656,270024,270443,270993:
Fix problems with orphan handling. Note that to preserve KBI, p_treeflag
has been added to the end of struct proc and is explicitly zero'd during
fork.
269656:
Correct the problems with the ptrace(2) making the debuggee an orphan.
One problem is inferior(9) looping due to the process tree becoming a
graph instead of tree if the parent is traced by child. Another issue
is due to the use of p_oppid to restore the original parent/child
relationship, because real parent could already exited and its pid
reused (noted by mjg).
Add the function proc_realparent(9), which calculates the parent for
given process. It uses the flag P_TREE_FIRST_ORPHAN to detect the head
element of the p_orphan list and than stepping back to its container
to find the parent process. If the parent has already exited, the
init(8) is returned.
Move the P_ORPHAN and the new helper flag from the p_flag* to new
p_treeflag field of struct proc, which is protected by proctree lock
instead of proc lock, since the orphans relationship is managed under
the proctree_lock already.
The remaining uses of p_oppid in ptrace(PT_DETACH) and process
reapping are replaced by proc_realparent(9).
270024:
Correct the order of arguments passed to LIST_INSERT_AFTER().
270443:
Properly reparent traced processes when the tracer dies.
Previously they were uncoditionally reparented to init. In effect
it was possible that tracee was never returned to original parent.
270993:
Fix up proc_realparent to always return correct process.
Prior to the change it would always return initproc for non-traced
processes.
Modified:
stable/9/bin/ps/ps.1
stable/9/sys/kern/kern_exit.c
stable/9/sys/kern/kern_fork.c
stable/9/sys/kern/kern_proc.c
stable/9/sys/kern/sys_process.c
stable/9/sys/sys/proc.h
Directory Properties:
stable/9/bin/ps/ (props changed)
stable/9/sys/ (props changed)
stable/9/sys/sys/ (props changed)
Modified: stable/9/bin/ps/ps.1
==============================================================================
--- stable/9/bin/ps/ps.1 Wed Sep 9 22:54:07 2015 (r287602)
+++ stable/9/bin/ps/ps.1 Wed Sep 9 23:05:52 2015 (r287603)
@@ -29,7 +29,7 @@
.\" @(#)ps.1 8.3 (Berkeley) 4/18/94
.\" $FreeBSD$
.\"
-.Dd June 6, 2014
+.Dd August 7, 2014
.Dt PS 1
.Os
.Sh NAME
@@ -332,7 +332,6 @@ the include file
.It Dv "P_SINGLE_BOUNDARY" Ta No "0x400000" Ta "Threads should suspend at user boundary"
.It Dv "P_HWPMC" Ta No "0x800000" Ta "Process is using HWPMCs"
.It Dv "P_JAILED" Ta No "0x1000000" Ta "Process is in jail"
-.It Dv "P_ORPHAN" Ta No "0x2000000" Ta "Orphaned by original parent, reparented to debugger"
.It Dv "P_INEXEC" Ta No "0x4000000" Ta "Process is in execve()"
.It Dv "P_STATCHILD" Ta No "0x8000000" Ta "Child process stopped or exited"
.It Dv "P_INMEM" Ta No "0x10000000" Ta "Loaded into memory"
Modified: stable/9/sys/kern/kern_exit.c
==============================================================================
--- stable/9/sys/kern/kern_exit.c Wed Sep 9 22:54:07 2015 (r287602)
+++ stable/9/sys/kern/kern_exit.c Wed Sep 9 23:05:52 2015 (r287603)
@@ -99,16 +99,48 @@ SDT_PROBE_DEFINE1(proc, kernel, , exit,
/* Hook for NFS teardown procedure. */
void (*nlminfo_release_p)(struct proc *p);
+struct proc *
+proc_realparent(struct proc *child)
+{
+ struct proc *p, *parent;
+
+ sx_assert(&proctree_lock, SX_LOCKED);
+ if ((child->p_treeflag & P_TREE_ORPHANED) == 0) {
+ if (child->p_oppid == 0 ||
+ child->p_pptr->p_pid == child->p_oppid)
+ parent = child->p_pptr;
+ else
+ parent = initproc;
+ return (parent);
+ }
+ for (p = child; (p->p_treeflag & P_TREE_FIRST_ORPHAN) == 0;) {
+ /* Cannot use LIST_PREV(), since the list head is not known. */
+ p = __containerof(p->p_orphan.le_prev, struct proc,
+ p_orphan.le_next);
+ KASSERT((p->p_treeflag & P_TREE_ORPHANED) != 0,
+ ("missing P_ORPHAN %p", p));
+ }
+ parent = __containerof(p->p_orphan.le_prev, struct proc,
+ p_orphans.lh_first);
+ return (parent);
+}
+
static void
clear_orphan(struct proc *p)
{
+ struct proc *p1;
- PROC_LOCK_ASSERT(p, MA_OWNED);
-
- if (p->p_flag & P_ORPHAN) {
- LIST_REMOVE(p, p_orphan);
- p->p_flag &= ~P_ORPHAN;
+ sx_assert(&proctree_lock, SA_XLOCKED);
+ if ((p->p_treeflag & P_TREE_ORPHANED) == 0)
+ return;
+ if ((p->p_treeflag & P_TREE_FIRST_ORPHAN) != 0) {
+ p1 = LIST_NEXT(p, p_orphan);
+ if (p1 != NULL)
+ p1->p_treeflag |= P_TREE_FIRST_ORPHAN;
+ p->p_treeflag &= ~P_TREE_FIRST_ORPHAN;
}
+ LIST_REMOVE(p, p_orphan);
+ p->p_treeflag &= ~P_TREE_ORPHANED;
}
/*
@@ -130,7 +162,8 @@ sys_sys_exit(struct thread *td, struct s
void
exit1(struct thread *td, int rv)
{
- struct proc *p, *nq, *q;
+ struct proc *p, *nq, *q, *t;
+ struct thread *tdt;
struct vnode *vtmp;
struct vnode *ttyvp = NULL;
struct plimit *plim;
@@ -419,7 +452,9 @@ exit1(struct thread *td, int rv)
WITNESS_WARN(WARN_PANIC, NULL, "process (pid %d) exiting", p->p_pid);
/*
- * Reparent all of our children to init.
+ * Reparent all children processes:
+ * - traced ones to the original parent (or init if we are that parent)
+ * - the rest to init
*/
sx_xlock(&proctree_lock);
q = LIST_FIRST(&p->p_children);
@@ -428,15 +463,23 @@ exit1(struct thread *td, int rv)
for (; q != NULL; q = nq) {
nq = LIST_NEXT(q, p_sibling);
PROC_LOCK(q);
- proc_reparent(q, initproc);
q->p_sigparent = SIGCHLD;
- /*
- * Traced processes are killed
- * since their existence means someone is screwing up.
- */
- if (q->p_flag & P_TRACED) {
- struct thread *temp;
+ if (!(q->p_flag & P_TRACED)) {
+ proc_reparent(q, initproc);
+ } else {
+ /*
+ * Traced processes are killed since their existence
+ * means someone is screwing up.
+ */
+ t = proc_realparent(q);
+ if (t == p) {
+ proc_reparent(q, initproc);
+ } else {
+ PROC_LOCK(t);
+ proc_reparent(q, t);
+ PROC_UNLOCK(t);
+ }
/*
* Since q was found on our children list, the
* proc_reparent() call moved q to the orphan
@@ -445,8 +488,8 @@ exit1(struct thread *td, int rv)
*/
clear_orphan(q);
q->p_flag &= ~(P_TRACED | P_STOPPED_TRACE);
- FOREACH_THREAD_IN_PROC(q, temp)
- temp->td_dbgflags &= ~TDB_SUSPEND;
+ FOREACH_THREAD_IN_PROC(q, tdt)
+ tdt->td_dbgflags &= ~TDB_SUSPEND;
kern_psignal(q, SIGKILL);
}
PROC_UNLOCK(q);
@@ -788,7 +831,9 @@ proc_reap(struct thread *td, struct proc
* If we got the child via a ptrace 'attach', we need to give it back
* to the old parent.
*/
- if (p->p_oppid && (t = pfind(p->p_oppid)) != NULL) {
+ if (p->p_oppid != 0) {
+ t = proc_realparent(p);
+ PROC_LOCK(t);
PROC_LOCK(p);
CTR2(KTR_PTRACE,
"wait: traced child %d moved back to parent %d", p->p_pid,
@@ -1269,8 +1314,15 @@ proc_reparent(struct proc *child, struct
clear_orphan(child);
if (child->p_flag & P_TRACED) {
- LIST_INSERT_HEAD(&child->p_pptr->p_orphans, child, p_orphan);
- child->p_flag |= P_ORPHAN;
+ if (LIST_EMPTY(&child->p_pptr->p_orphans)) {
+ child->p_treeflag |= P_TREE_FIRST_ORPHAN;
+ LIST_INSERT_HEAD(&child->p_pptr->p_orphans, child,
+ p_orphan);
+ } else {
+ LIST_INSERT_AFTER(LIST_FIRST(&child->p_pptr->p_orphans),
+ child, p_orphan);
+ }
+ child->p_treeflag |= P_TREE_ORPHANED;
}
child->p_pptr = parent;
Modified: stable/9/sys/kern/kern_fork.c
==============================================================================
--- stable/9/sys/kern/kern_fork.c Wed Sep 9 22:54:07 2015 (r287602)
+++ stable/9/sys/kern/kern_fork.c Wed Sep 9 23:05:52 2015 (r287603)
@@ -408,6 +408,7 @@ do_fork(struct thread *td, int flags, st
bzero(&p2->p_startzero,
__rangeof(struct proc, p_startzero, p_endzero));
+ p2->p_treeflag = 0;
p2->p_ucred = crhold(td->td_ucred);
Modified: stable/9/sys/kern/kern_proc.c
==============================================================================
--- stable/9/sys/kern/kern_proc.c Wed Sep 9 22:54:07 2015 (r287602)
+++ stable/9/sys/kern/kern_proc.c Wed Sep 9 23:05:52 2015 (r287603)
@@ -263,14 +263,15 @@ proc_fini(void *mem, int size)
* Is p an inferior of the current process?
*/
int
-inferior(p)
- register struct proc *p;
+inferior(struct proc *p)
{
sx_assert(&proctree_lock, SX_LOCKED);
- for (; p != curproc; p = p->p_pptr)
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ for (; p != curproc; p = proc_realparent(p)) {
if (p->p_pid == 0)
return (0);
+ }
return (1);
}
Modified: stable/9/sys/kern/sys_process.c
==============================================================================
--- stable/9/sys/kern/sys_process.c Wed Sep 9 22:54:07 2015 (r287602)
+++ stable/9/sys/kern/sys_process.c Wed Sep 9 23:05:52 2015 (r287603)
@@ -951,19 +951,11 @@ kern_ptrace(struct thread *td, int req,
case PT_DETACH:
/* reset process parent */
if (p->p_oppid != p->p_pptr->p_pid) {
- struct proc *pp;
-
PROC_LOCK(p->p_pptr);
sigqueue_take(p->p_ksi);
PROC_UNLOCK(p->p_pptr);
- PROC_UNLOCK(p);
- pp = pfind(p->p_oppid);
- if (pp == NULL)
- pp = initproc;
- else
- PROC_UNLOCK(pp);
- PROC_LOCK(p);
+ pp = proc_realparent(p);
proc_reparent(p, pp);
if (pp == initproc)
p->p_sigparent = SIGCHLD;
Modified: stable/9/sys/sys/proc.h
==============================================================================
--- stable/9/sys/sys/proc.h Wed Sep 9 22:54:07 2015 (r287602)
+++ stable/9/sys/sys/proc.h Wed Sep 9 23:05:52 2015 (r287603)
@@ -593,6 +593,7 @@ struct proc {
LIST_ENTRY(proc) p_orphan; /* (e) List of orphan processes. */
LIST_HEAD(, proc) p_orphans; /* (e) Pointer to list of orphans. */
u_char p_throttled; /* (c) Flag for racct pcpu throttling */
+ u_int p_treeflag; /* (e) P_TREE flags */
};
#define p_session p_pgrp->pg_session
@@ -630,7 +631,7 @@ struct proc {
#define P_SINGLE_BOUNDARY 0x400000 /* Threads should suspend at user boundary. */
#define P_HWPMC 0x800000 /* Process is using HWPMCs */
#define P_JAILED 0x1000000 /* Process is in jail. */
-#define P_ORPHAN 0x2000000 /* Orphaned. */
+#define P_UNUSED1 0x2000000
#define P_INEXEC 0x4000000 /* Process is in execve(). */
#define P_STATCHILD 0x8000000 /* Child process stopped or exited. */
#define P_INMEM 0x10000000 /* Loaded into memory. */
@@ -645,6 +646,11 @@ struct proc {
/* These flags are kept in p_flag2. */
#define P2_INHERIT_PROTECTED 0x00000001 /* New children get P_PROTECTED. */
+/* Flags protected by proctree_lock, kept in p_treeflags. */
+#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
+#define P_TREE_FIRST_ORPHAN 0x00000002 /* First element of orphan
+ list */
+
/*
* These were process status values (p_stat), now they are only used in
* legacy conversion code.
@@ -891,6 +897,7 @@ int proc_getenvv(struct thread *td, stru
void procinit(void);
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_reparent(struct proc *child, struct proc *newparent);
struct pstats *pstats_alloc(void);
More information about the svn-src-stable-9
mailing list