summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2014-08-07 05:47:53 +0000
committerkib <kib@FreeBSD.org>2014-08-07 05:47:53 +0000
commit0b059d23c7081e1cea5512459c573223dc017be9 (patch)
tree033a19954132b23984c24a614f928220b364cc2b /sys/kern
parentffe6b11ba6dedd5eff6feb31b9e53c526bf6c5dd (diff)
downloadFreeBSD-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.c53
-rw-r--r--sys/kern/kern_proc.c7
-rw-r--r--sys/kern/sys_process.c10
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;
OpenPOWER on IntegriCloud