lkml.org 
[lkml]   [2000]   [Dec]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
SubjectRe: Pthreads, linux, gdb, oh my!
From
Date
Peter Berger <peterb@telerama.com> writes:

> > > The zombie problem is a kernel bug. AFAIK there is no kernel that
> > > doesn't have this bug. Unfortunately getting the bugfix in isn't very
> > > easy. I'll try poking Linus again when I can find the time.
>
> *sob*
>
> Alan Cox says it is a glibc bug.

Well, he says it's likely to be a glibc bug, and from the information
you've given him I don't blame him for coming to that conclusion.

However, the "zombie problem" is caused by the way ptrace() interacts
with clone()/exit()/wait(), which I consider to be a kernel bug. Let
me explain:

When LinuxThreads (the glibc pthreads implementation) creates a new
thread using the clone() system call, it arranges for itself to get a
special "cancel" signal to be delivered when the newly thread exits
(instead of SIGCHLD), such that it is possible to distinguish between
exiting threads and genuine child processes. When it recieves this
special "cancel" signal it calls wait() with the __WCLONE flag such
that exited threads (and only exited threads, not exited child
processes) are reaped and don't live on as zombies.

When run under GDB, the debugger attaches to every newly created
thread (using ptrace()). This has the effect that the thread's parent
is now the debugger instead of the special manager thread that is
supposed to reap that particular thread when it exits. So the event
is reported to GDB (with the special "cancel" signal), and GDB does
the wait() with the __WCLONE flag that's necessary to detach from the
exited thread. However the kernel notices that the exited thread was
being traced and keeps it around as a zombie to give the LinuxThreads
library the opportunity to wait() for it. Unfortunately the kernel
unconditionally sends a SIGCHLD instead of the special "cancel"
signal, and the event goes unnoticed, as can be seen from the
following bit of code in linux/kernel/exit.c:sys_wait4():

if (p->p_opptr != p->p_pptr) {
write_lock_irq(&tasklist_lock);
REMOVE_LINKS(p);
p->p_pptr = p->p_opptr;
SET_LINKS(p);
write_unlock_irq(&tasklist_lock);
notify_parent(p, SIGCHLD);
} else
release(p);

AFAICT all officially released kernels have this problem.

Furthermore, there is a problem with some kernels (the 2.4 series and
the latest kernels in the 2.2 series), where the special exit signal
is being reset to SIGCHLD by the following code in
linux/kernel/exit.c:exit_notify():

/* Let father know we died
*
* Thread signals are configurable, but you aren't going to use
* that to send signals to arbitary processes.
* That stops right now.
*
* If the parent exec id doesn't match the exec id we saved
* when we started then we know the parent has changed security
* domain.
*
* If our self_exec id doesn't match our parent_exec_id then
* we have changed execution domain as these two values started
* the same after a fork.
*
*/

if(current->exit_signal != SIGCHLD &&
( current->parent_exec_id != t->self_exec_id ||
current->self_exec_id != current->parent_exec_id)
&& !capable(CAP_KILL))
current->exit_signal = SIGCHLD;

since current->parent_exit_id and t->self_exec_id don't match for
traced processes (since t->self_exec_id is the exec id of the debugger
and not of the origional parent). This means that even if the
LinuxThreads library receives its special "cancel" signal, it won't
notice the exited threads (since it uses wait() with the __WCLONE
flag), and those threads will live on as zombies until the entire
process exits.

I'm not sure how to fix this problem. I would say that the attached
patch (against Linux 2.2.18) should fix things, but I'm not entirely
confident that it doesn't open a security hole. Oh, and I didn't test
this exact patch, I just tested something similar some time ago.

Mark


--- exit.c.orig Tue Jan 4 19:12:25 2000
+++ exit.c Wed Dec 13 14:36:24 2000
@@ -291,7 +291,7 @@
* is about to become orphaned.
*/

- t = current->p_pptr;
+ t = current->p_opptr;

if ((t->pgrp != current->pgrp) &&
(t->session == current->session) &&
@@ -497,7 +497,7 @@
p->p_pptr = p->p_opptr;
SET_LINKS(p);
write_unlock_irq(&tasklist_lock);
- notify_parent(p, SIGCHLD);
+ notify_parent(p, p->exit_signal);
} else
release(p);
#ifdef DEBUG_PROC_TREE

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 13:28    [W:0.060 / U:7.836 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site