/* Copyright (C) 2004 Hewlett-Packard Development Co, LLC Contributed by David Mosberger-Tang This program tests if a self-reaping task correctly gets released if it exits while being ptraced by another program. As of Linux kernel v2.6.7, the task incorrectly remains in an unkillable zombie state. */ #include #include #include #include #include #include #include #include #ifndef PTRACE_EVENT_FORK # define PTRACE_EVENT_FORK 1 # define PTRACE_EVENT_VFORK 2 # define PTRACE_EVENT_CLONE 3 # define PTRACE_EVENT_EXEC 4 # define PTRACE_EVENT_VFORK_DONE 5 # define PTRACE_EVENT_EXIT 6 # define PTRACE_SETOPTIONS 0x4200 # define PTRACE_GETEVENTMSG 0x4201 # define PTRACE_O_TRACESYSGOOD 0x00000001 # define PTRACE_O_TRACEFORK 0x00000002 # define PTRACE_O_TRACEVFORK 0x00000004 # define PTRACE_O_TRACECLONE 0x00000008 # define PTRACE_O_TRACEEXEC 0x00000010 # define PTRACE_O_TRACEVFORKDONE 0x00000020 # define PTRACE_O_TRACEEXIT 0x00000040 #endif static pid_t child_pid; static int child (void) { printf ("[%d] child sending SIGINT to self\n", getpid ()); kill (getpid (), SIGINT); return -3; } static int parent (void) { struct sigaction act; int ret; printf ("[%d] parent starting\n", getpid ()); /* ignore SIGCHLD */ memset (&act, 0, sizeof (act)); act.sa_handler = SIG_IGN; act.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT; sigaction(SIGCHLD, &act, NULL); child_pid = fork (); if (child_pid == 0) exit (child ()); printf ("[%d] forked child %d\n", getpid (), child_pid); // sleep (1); ret = wait (NULL); if (ret == -1) printf ("[%d] wait() failed as expected: %s\n", getpid (), strerror (errno)); printf ("[%d] parent exiting\n", getpid ()); return 0; } static int controller (void) { int status, pending_sig, event; pid_t pid; printf ("[%d] controller starting\n", getpid ()); while (1) { pending_sig = 0; pid = wait4 (-1, &status, 0, 0); if (pid == -1) { if (errno == EINTR) continue; printf ("[%d] wait4() failed (errno=%d)\n", getpid (), errno); } if (WIFSTOPPED (status)) { if (WSTOPSIG (status) == SIGTRAP) { pending_sig = 0; /* don't propagate signal */ event = (status >> 16) & 0xffff; switch (event) { case PTRACE_EVENT_FORK: { long new_pid; ptrace (PTRACE_GETEVENTMSG, pid, NULL, &new_pid); printf ("[%d] got new pid %ld\n", getpid (), new_pid); child_pid = new_pid; } break; case 0: printf ("[%d] setting TRACEFORK option for %d\n", getpid (), pid); ptrace (PTRACE_SETOPTIONS, pid, NULL, (unsigned long) PTRACE_O_TRACEFORK); break; default: printf ("[%d] got unexpected event 0x%x\n", getpid (), event); break; } } else if (WSTOPSIG (status) == SIGSTOP) { printf ("[%d] ignoring SIGSTOP for pid %d\n", getpid (), pid); pending_sig = 0; } else { pending_sig = WSTOPSIG (status); printf ("[%d] pid %d got signal %d\n", getpid (), pid, pending_sig); if (pending_sig == SIGSEGV) exit (-1); } } else if (WIFEXITED (status)) { printf ("[%d] pid %d exit status %d\n", getpid (), pid, WEXITSTATUS (status)); break; } else if (WIFSIGNALED (status)) { printf ("[%d] pid %d terminated by signal %d\n", getpid (), pid, WTERMSIG (status)); } else printf ("[%d] Duh?\n", getpid ()); ptrace (PTRACE_CONT, pid, 0, pending_sig); /* continue */ } printf ("[%d] controller exiting\n", getpid ()); if (kill (child_pid, SIGTERM) != -1) { printf ("FAILURE: child %d seems to be still around!\n", child_pid); return -1; } return 0; } int main (int argc, char **argv) { pid_t parent_pid; parent_pid = fork (); if (parent_pid == 0) { ptrace (PTRACE_TRACEME, 0, 0, 0); kill (getpid (), SIGTRAP); /* since we don't do execve()... */ exit (parent ()); } return controller (); }