diff options
author | kib <kib@FreeBSD.org> | 2014-08-07 05:47:53 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2014-08-07 05:47:53 +0000 |
commit | 0b059d23c7081e1cea5512459c573223dc017be9 (patch) | |
tree | 033a19954132b23984c24a614f928220b364cc2b /sys/kern | |
parent | ffe6b11ba6dedd5eff6feb31b9e53c526bf6c5dd (diff) | |
download | FreeBSD-src-0b059d23c7081e1cea5512459c573223dc017be9.zip FreeBSD-src-0b059d23c7081e1cea5512459c573223dc017be9.tar.gz |
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).
Phabric: D417
Reviewed by: jhb
Tested by: pho
Sponsored by: The FreeBSD Foundation
MFC after: 2 weeks
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_exit.c | 53 | ||||
-rw-r--r-- | sys/kern/kern_proc.c | 7 | ||||
-rw-r--r-- | sys/kern/sys_process.c | 10 |
3 files changed, 50 insertions, 20 deletions
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 9efaac2..8dad2ad 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -97,16 +97,44 @@ SDT_PROBE_DEFINE1(proc, kernel, , exit, "int"); /* 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) { + return (child->p_pptr->p_pid == child->p_oppid ? + child->p_pptr : initproc); + } + 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; } /* @@ -772,7 +800,9 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options) * 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); proc_reparent(p, t); p->p_oppid = 0; @@ -1243,8 +1273,15 @@ proc_reparent(struct proc *child, struct proc *parent) 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(child, + LIST_FIRST(&child->p_pptr->p_orphans), p_orphan); + } + child->p_treeflag |= P_TREE_ORPHANED; } child->p_pptr = parent; diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index a4378b4..6689186 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -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); } diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 821c779..b30e12a 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -917,19 +917,11 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) 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; |