#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #define fail(msg, args...) \ do { \ fprintf(stderr, "FAIL:%i: " msg "\n", __LINE__, ## args); \ exit(1); \ } while (0) static void child_exit(int sig) { int status; #if 0 /* Correct behavior. */ printf("failure, child exited with %i: %s\n", sig, strsignal(sig)); #endif printf("wait() = %i\n", wait(&status)); printf("status = 0x%x\n", status); printf("\tWIFEXITED = %i\n", WIFEXITED(status)); printf("\tWEXITSTATUS = %i\n", WEXITSTATUS(status)); printf("\tWIFSIGNALED = %i\n", WIFSIGNALED(status)); printf("\tWTERMSIG = %i (%s)\n", WTERMSIG(status), strsignal(WTERMSIG(status))); /* WIFSTOPPED happens. */ printf("\tWIFSTOPPED = %i\n", WIFSTOPPED(status)); /* SIGTRAP happens. */ printf("\tWSTOPSIG = %i (%s)\n", WSTOPSIG(status), strsignal(WSTOPSIG(status))); #if 0 /* We can continue. Just calling printf() from a signal handler is not correct. */ exit(1); #endif } int main(int argc, char *argv[]) { long pret; pid_t pid; /* child process ... shouldnt be executed, but just in case ... */ if (argc > 1 && !strcmp(argv[1], "child")) #if 0 /* Parent did not kill us, after its child_exit() messages we should get here. */ fail("kernel should have halted me..."); #else { puts ("child exiting"); exit (0); } #endif /* vfork() child must not call ptrace(). */ pid = fork(); if (pid == -1) fail("vfork() didnt work: %m"); else if (!pid) { /* do the child stuff here */ errno = 0; pret = ptrace(PTRACE_TRACEME, 0, NULL, NULL); if (pret && errno) fail("ptrace(PTRACE_TRACEME) = %li: %m", pret); int eret = execlp(argv[0], argv[0], "child", NULL); fail("execlp() = %i", eret); } /* do the parent stuff here */ signal(SIGCHLD, child_exit); /* We cannot PTRACE_PEEKUSER here as the child still may not have called PTRACE_TRACEME. */ pause (); errno = 0; pret = ptrace(PTRACE_PEEKUSER, pid, NULL, NULL); if (pret && errno) fail("ptrace(PTRACE_PEEKUSER, %i) = %li: %m", pid, pret); puts("SUCCESS! :D"); return 0; }