/* ****************************************************************************** * * ptrace05 - an app which ptraces itself as per arbitrarily specified signals, * over a user specified range. * * Copyright (C) 2009, Ngie Cooper * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ****************************************************************************** */ #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <errno.h> #include <libgen.h> #include <math.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <config.h> #include "ptrace.h" #include "test.h" char *TCID = "ptrace05"; int TST_TOTAL = 0; int usage(const char *); int usage(const char *argv0) { fprintf(stderr, "usage: %s [start-signum] [end-signum]\n", argv0); return 1; } int main(int argc, char **argv) { int end_signum = -1; int signum; int start_signum = -1; int status; pid_t child; tst_parse_opts(argc, argv, NULL, NULL); if (start_signum == -1) { start_signum = 0; } if (end_signum == -1) { end_signum = SIGRTMAX; } for (signum = start_signum; signum <= end_signum; signum++) { switch (child = fork()) { case -1: tst_brkm(TBROK | TERRNO, NULL, "fork() failed"); case 0: if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != -1) { tst_resm(TINFO, "[child] Sending kill(.., %d)", signum); if (kill(getpid(), signum) < 0) { tst_resm(TINFO | TERRNO, "[child] kill(.., %d) failed.", signum); } } else { /* * This won't increment the TST_COUNT var. * properly, but it'll show up as a failure * nonetheless. */ tst_resm(TFAIL | TERRNO, "Failed to ptrace(PTRACE_TRACEME, ...) " "properly"); } /* Shouldn't get here if signum == 0. */ exit((signum == 0 ? 0 : 2)); break; default: waitpid(child, &status, 0); switch (signum) { case 0: if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { tst_resm(TPASS, "kill(.., 0) exited " "with 0, as expected."); } else { tst_resm(TFAIL, "kill(.., 0) didn't exit " "with 0."); } break; case SIGKILL: if (WIFSIGNALED(status)) { /* SIGKILL must be uncatchable. */ if (WTERMSIG(status) == SIGKILL) { tst_resm(TPASS, "Killed with SIGKILL, " "as expected."); } else { tst_resm(TPASS, "Didn't die with " "SIGKILL (?!) "); } } else if (WIFEXITED(status)) { tst_resm(TFAIL, "Exited unexpectedly instead " "of dying with SIGKILL."); } else if (WIFSTOPPED(status)) { tst_resm(TFAIL, "Stopped instead of dying " "with SIGKILL."); } break; /* All other processes should be stopped. */ default: if (WIFSTOPPED(status)) { tst_resm(TPASS, "Stopped as expected"); } else { tst_resm(TFAIL, "Didn't stop as " "expected."); if (kill(child, 0)) { tst_resm(TINFO, "Is still alive!?"); } else if (WIFEXITED(status)) { tst_resm(TINFO, "Exited normally"); } else if (WIFSIGNALED(status)) { tst_resm(TINFO, "Was signaled with " "signum=%d", WTERMSIG(status)); } } break; } } /* Make sure the child dies a quick and painless death ... */ kill(child, 9); } tst_exit(); }