1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. 4 * Copyright (c) 2019 SUSE LLC 5 * 6 * Author: Saji Kumar.V.R <saji.kumar@wipro.com> 7 * Ported to new library: Jorik Cronenberg <jcronenberg@suse.de> 8 * 9 * Test the functionality of ptrace() for PTRACE_TRACEME in combination with 10 * PTRACE_KILL and PTRACE_CONT requests. 11 * Forked child does ptrace(PTRACE_TRACEME, ...). 12 * Then a signal is delivered to the child and verified that parent 13 * is notified via wait(). 14 * Afterwards parent does ptrace(PTRACE_KILL, ..)/ptrace(PTRACE_CONT, ..) 15 * and then parent does wait() for child to finish. 16 * Test passes if child exits with SIGKILL for PTRACE_KILL. 17 * Test passes if child exits normally for PTRACE_CONT. 18 * 19 * Testing two cases for each: 20 * 1) child ignore SIGUSR2 signal 21 * 2) using a signal handler for child for SIGUSR2 22 * In both cases, child should stop & notify parent on reception of SIGUSR2. 23 */ 24 25#include <stdlib.h> 26#include <errno.h> 27#include <signal.h> 28#include <sys/wait.h> 29#include <config.h> 30#include "ptrace.h" 31#include "tst_test.h" 32 33static struct tcase { 34 int handler; 35 int request; 36 int exp_wifexited; 37 int exp_wtermsig; 38 char *message; 39} tcases[] = { 40 {0, PTRACE_KILL, 0, 9, "Testing PTRACE_KILL without child handler"}, 41 {1, PTRACE_KILL, 0, 9, "Testing PTRACE_KILL with child handler"}, 42 {0, PTRACE_CONT, 1, 0, "Testing PTRACE_CONT without child handler"}, 43 {1, PTRACE_CONT, 1, 0, "Testing PTRACE_CONT with child handler"}, 44}; 45 46static volatile int got_signal; 47 48static void child_handler(int sig LTP_ATTRIBUTE_UNUSED) 49{ 50 SAFE_KILL(getppid(), SIGUSR2); 51} 52 53static void parent_handler(int sig LTP_ATTRIBUTE_UNUSED) 54{ 55 got_signal = 1; 56} 57 58static void do_child(unsigned int i) 59{ 60 struct sigaction child_act; 61 62 if (i == 0) 63 child_act.sa_handler = SIG_IGN; 64 else 65 child_act.sa_handler = child_handler; 66 67 child_act.sa_flags = SA_RESTART; 68 sigemptyset(&child_act.sa_mask); 69 70 SAFE_SIGACTION(SIGUSR2, &child_act, NULL); 71 72 if ((ptrace(PTRACE_TRACEME, 0, 0, 0)) == -1) { 73 tst_res(TWARN, "ptrace() failed in child"); 74 exit(1); 75 } 76 SAFE_KILL(getpid(), SIGUSR2); 77 exit(1); 78} 79 80static void run(unsigned int i) 81{ 82 struct tcase *tc = &tcases[i]; 83 pid_t child_pid; 84 int status; 85 struct sigaction parent_act; 86 87 got_signal = 0; 88 89 tst_res(TINFO, "%s", tc->message); 90 91 if (tc->handler == 1) { 92 parent_act.sa_handler = parent_handler; 93 parent_act.sa_flags = SA_RESTART; 94 sigemptyset(&parent_act.sa_mask); 95 SAFE_SIGACTION(SIGUSR2, &parent_act, NULL); 96 } 97 98 child_pid = SAFE_FORK(); 99 100 if (!child_pid) 101 do_child(tc->handler); 102 103 SAFE_WAITPID(child_pid, &status, 0); 104 105 if (((WIFEXITED(status)) && (WEXITSTATUS(status))) 106 || (got_signal == 1)) 107 tst_res(TFAIL, "Test Failed"); 108 else if ((ptrace(tc->request, child_pid, 0, 0)) == -1) 109 tst_res(TFAIL | TERRNO, "ptrace(%i, %i, 0, 0) failed", 110 tc->request, child_pid); 111 112 SAFE_WAITPID(child_pid, &status, 0); 113 114 if ((tc->request == PTRACE_CONT && 115 WIFEXITED(status) && WEXITSTATUS(status) == tc->exp_wifexited) || 116 (tc->request == PTRACE_KILL && 117 WIFSIGNALED(status) && WTERMSIG(status) == tc->exp_wtermsig)) { 118 tst_res(TPASS, "Child %s as expected", tst_strstatus(status)); 119 } else { 120 tst_res(TFAIL, "Child %s unexpectedly", tst_strstatus(status)); 121 } 122 123} 124 125static struct tst_test test = { 126 .test = run, 127 .tcnt = ARRAY_SIZE(tcases), 128 .forks_child = 1, 129}; 130