I am experiencing problems with ptrace() under FreeBSD. I made a simple example program to demonstrate. All it does is fork a child process to execl() a simple "hello world" program and ptrace() it with PT_CONTINUE. The first time around, everything is as it should be - the program is executed and traced perfectly. However, when I attempt to repeat the whole process, first by forking a completely new child to execute the helloworld program again, I start getting ptrace() errors such as "Operation not permitted" and "Device busy". Now the weird thing is, after the first trace, the program terminates normally, and I print out the pid of the child process which should no longer be running at this point. ptrace(PT_KILL) verifies this by returning "No such process". However, during the pause between the first and second ptrace, if I do a ps and search for the pid, it shows up in memory as running in the background, although ptrace(PT_KILL) claims it does not exist. Then my sample program attempts to trace the same helloworld program again, and gets "Operation not permitted" - even though an entirely new child is fork()'d with an entirely new pid - so it should have absolutely no connection at all to the first trace. Does anyone know why I can't ptrace() the same helloworld program a second time? I have attached my sample ptrace() program to this message. Thanks in advance for any help Patrick Alken
/* * To run this, make a prog.c which contains: * int main() { printf("hello world\n"); } * and compile it into the file "prog". * * Then, simply: gcc -o myptrace myptrace.c * and ./myptrace */ #include <stdio.h> #include <sys/types.h> #include <sys/ptrace.h> #include <errno.h> #include <sys/wait.h> #include <machine/reg.h> #include <unistd.h> #include <string.h> /* DoTrace() - begin to trace a process */ int DoTrace() { int pid; /* child pid */ int waitval; struct reg regs; pid = fork(); switch (pid) { case -1: { perror("fork"); break; } /* * Child */ case 0: { /* * Allow parent to trace this child process */ ptrace(PT_TRACE_ME, 0, 0, 0); /* * Execute program to be debugged and cause child to * send a signal to parent */ execl("./prog", "prog", NULL); exit(0); } /* * Parent */ default: { int pret; /* * Wait for child to stop (execl) */ wait(&waitval); printf("waitval = %d\n", waitval); /* * Continue exection of process */ pret = ptrace(PT_CONTINUE, pid, (caddr_t) 1, 0); if (pret != 0) { /* * This is where it fails the second time with an * errno of 1 (EPERM), even though we are tracing * a completely new pid. */ perror("ptrace"); } wait(&waitval); pret = ptrace(PT_KILL, pid, 0, 0); if (pret == 0) { printf("Kill successful\n"); wait(&waitval); } else printf("Kill unsuccessful, errno = %s\n", strerror(errno)); } } return (pid); } /* DoTrace() */ int main() { int pid; char buf[512]; /* * Trace through the process */ pid = DoTrace(); printf("Pid1 = %d\n", pid); fgets(buf, 512, stdin); /* * Trace through again (this is where it fails) */ pid = DoTrace(); printf("Pid2 = %d\n", pid); return 0; }