/* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /********************************************************** * * TEST IDENTIFIER : ptrace01 * * EXECUTED BY : anyone * * TEST TITLE : functionality test for ptrace(2) * * TEST CASE TOTAL : 2 * * AUTHOR : Saji Kumar.V.R <saji.kumar@wipro.com> * * SIGNALS * Uses SIGUSR1 to pause before test if option set. * (See the parse_opts(3) man page). * * DESCRIPTION * This test case tests the functionality of ptrace() for * PTRACE_TRACEME & PTRACE_KILL requests. * Here, we fork a child & the child does ptrace(PTRACE_TRACEME, ...). * Then a signal is delivered to the child & verified that parent * is notified via wait(). then parent does ptrace(PTRACE_KILL, ..) * to kill the child. Again parent wait() for child to finish. * If child finished abnormally, test passes. * We test two cases * 1) By telling child to ignore SIGUSR2 signal * 2) By installing a signal handler for child for SIGUSR2 * In both cases, child should stop & notify parent on reception * of SIGUSR2 * * Setup: * Setup signal handling. * Pause for SIGUSR1 if option specified. * * Test: * Loop if the proper options are given. * setup signal handler for SIGUSR2 signal * fork a child * * CHILD: * setup signal handler for SIGUSR2 signal * call ptrace() with PTRACE_TRACEME request * send SIGUSR2 signal to self * PARENT: * wait() for child. * if parent is notified when child gets a signal through wait(), * then * do ptrace(PTRACE_KILL, ..) on child * wait() for child to finish, * if child exited abnormaly, * TEST passed * else * TEST failed * else * TEST failed * * Cleanup: * Print errno log and/or timing stats if options given * * USAGE: <for command-line> * ptrace01 [-c n] [-e] [-i n] [-I x] [-P x] [-t] [-h] [-f] [-p] * where, -c n : Run n copies concurrently. * -e : Turn on errno logging. * -h : Show help screen * -f : Turn off functional testing * -i n : Execute test n times. * -I x : Execute test for x seconds. * -p : Pause for SIGUSR1 before starting * -P x : Pause for x seconds between iterations. * -t : Turn on syscall timing. * ****************************************************************/ #include <errno.h> #include <signal.h> #include <sys/wait.h> #include <config.h> #include "ptrace.h" #include "test.h" static void do_child(void); static void setup(void); static void cleanup(void); static void child_handler(); static void parent_handler(); static int got_signal = 0; char *TCID = "ptrace01"; static int i; /* loop test case counter, shared with do_child */ int TST_TOTAL = 2; int main(int ac, char **av) { int lc; pid_t child_pid; int status; struct sigaction parent_act; tst_parse_opts(ac, av, NULL, NULL); #ifdef UCLINUX maybe_run_child(&do_child, "d", &i); #endif setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { tst_count = 0; for (i = 0; i < TST_TOTAL; ++i) { got_signal = 0; /* Setup signal handler for parent */ if (i == 1) { parent_act.sa_handler = parent_handler; parent_act.sa_flags = SA_RESTART; sigemptyset(&parent_act.sa_mask); if ((sigaction(SIGUSR2, &parent_act, NULL)) == -1) { tst_resm(TWARN, "sigaction() failed" " in parent"); continue; } } switch (child_pid = FORK_OR_VFORK()) { case -1: /* fork() failed */ tst_resm(TFAIL, "fork() failed"); continue; case 0: /* Child */ #ifdef UCLINUX if (self_exec(av[0], "d", i) < 0) { tst_resm(TFAIL, "self_exec failed"); continue; } #else do_child(); #endif default: /* Parent */ if ((waitpid(child_pid, &status, 0)) < 0) { tst_resm(TFAIL, "waitpid() failed"); continue; } /* * Check the exit status of child. If (it exits * normally with exit value 1) OR (child came * through signal handler), Test Failed */ if (((WIFEXITED(status)) && (WEXITSTATUS(status))) || (got_signal == 1)) { tst_resm(TFAIL, "Test Failed"); continue; } else { /* Kill child */ if ((ptrace(PTRACE_KILL, child_pid, 0, 0)) == -1) { tst_resm(TFAIL, "Test Failed:" " Parent was not able to kill" " child"); continue; } } if ((waitpid(child_pid, &status, 0)) < 0) { tst_resm(TFAIL, "waitpid() failed"); continue; } if (WIFEXITED(status)) { /* Child exits normally */ tst_resm(TFAIL, "Test failed"); } else { tst_resm(TPASS, "Test Passed"); } } } } /* cleanup and exit */ cleanup(); tst_exit(); } /* do_child() */ void do_child(void) { struct sigaction child_act; /* Setup signal handler for child */ if (i == 0) { child_act.sa_handler = SIG_IGN; } else { child_act.sa_handler = child_handler; } child_act.sa_flags = SA_RESTART; sigemptyset(&child_act.sa_mask); if ((sigaction(SIGUSR2, &child_act, NULL)) == -1) { tst_resm(TWARN, "sigaction() failed in child"); exit(1); } if ((ptrace(PTRACE_TRACEME, 0, 0, 0)) == -1) { tst_resm(TWARN, "ptrace() failed in child"); exit(1); } /* ensure that child bypasses signal handler */ if ((kill(getpid(), SIGUSR2)) == -1) { tst_resm(TWARN, "kill() failed in child"); exit(1); } exit(1); } /* setup() - performs all ONE TIME setup for this test */ void setup(void) { tst_sig(FORK, DEF_HANDLER, cleanup); TEST_PAUSE; } /* *cleanup() - performs all ONE TIME cleanup for this test at * completion or premature exit. */ void cleanup(void) { } /* * child_handler() - Signal handler for child */ void child_handler(void) { if ((kill(getppid(), SIGUSR2)) == -1) { tst_resm(TWARN, "kill() failed in child_handler()"); exit(1); } } /* * parent_handler() - Signal handler for parent */ void parent_handler(void) { got_signal = 1; }