1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved.
4 * Copyright (c) 2019 SUSE LLC
5 *
6 * Author: Saji Kumar.V.R <[email protected]>
7 * Ported to new library: Jorik Cronenberg <[email protected]>
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 <sys/ptrace.h>
30 #include "tst_test.h"
31
32 static struct tcase {
33 int handler;
34 int request;
35 int exp_wifexited;
36 int exp_wtermsig;
37 char *message;
38 } tcases[] = {
39 {0, PTRACE_KILL, 0, 9, "Testing PTRACE_KILL without child handler"},
40 {1, PTRACE_KILL, 0, 9, "Testing PTRACE_KILL with child handler"},
41 {0, PTRACE_CONT, 1, 0, "Testing PTRACE_CONT without child handler"},
42 {1, PTRACE_CONT, 1, 0, "Testing PTRACE_CONT with child handler"},
43 };
44
45 static volatile int got_signal;
46
child_handler(int sig LTP_ATTRIBUTE_UNUSED)47 static void child_handler(int sig LTP_ATTRIBUTE_UNUSED)
48 {
49 SAFE_KILL(getppid(), SIGUSR2);
50 }
51
parent_handler(int sig LTP_ATTRIBUTE_UNUSED)52 static void parent_handler(int sig LTP_ATTRIBUTE_UNUSED)
53 {
54 got_signal = 1;
55 }
56
do_child(unsigned int i)57 static void do_child(unsigned int i)
58 {
59 struct sigaction child_act;
60
61 if (i == 0)
62 child_act.sa_handler = SIG_IGN;
63 else
64 child_act.sa_handler = child_handler;
65
66 child_act.sa_flags = SA_RESTART;
67 sigemptyset(&child_act.sa_mask);
68
69 SAFE_SIGACTION(SIGUSR2, &child_act, NULL);
70
71 if ((ptrace(PTRACE_TRACEME, 0, 0, 0)) == -1) {
72 tst_res(TWARN, "ptrace() failed in child");
73 exit(1);
74 }
75 SAFE_KILL(getpid(), SIGUSR2);
76 exit(1);
77 }
78
run(unsigned int i)79 static void run(unsigned int i)
80 {
81 struct tcase *tc = &tcases[i];
82 pid_t child_pid;
83 int status;
84 struct sigaction parent_act;
85
86 got_signal = 0;
87
88 tst_res(TINFO, "%s", tc->message);
89
90 if (tc->handler == 1) {
91 parent_act.sa_handler = parent_handler;
92 parent_act.sa_flags = SA_RESTART;
93 sigemptyset(&parent_act.sa_mask);
94 SAFE_SIGACTION(SIGUSR2, &parent_act, NULL);
95 }
96
97 child_pid = SAFE_FORK();
98
99 if (!child_pid)
100 do_child(tc->handler);
101
102 SAFE_WAITPID(child_pid, &status, 0);
103
104 if (((WIFEXITED(status)) && (WEXITSTATUS(status)))
105 || (got_signal == 1))
106 tst_res(TFAIL, "Test Failed");
107 else if ((ptrace(tc->request, child_pid, 0, 0)) == -1)
108 tst_res(TFAIL | TERRNO, "ptrace(%i, %i, 0, 0) failed",
109 tc->request, child_pid);
110
111 SAFE_WAITPID(child_pid, &status, 0);
112
113 if ((tc->request == PTRACE_CONT &&
114 WIFEXITED(status) && WEXITSTATUS(status) == tc->exp_wifexited) ||
115 (tc->request == PTRACE_KILL &&
116 WIFSIGNALED(status) && WTERMSIG(status) == tc->exp_wtermsig)) {
117 tst_res(TPASS, "Child %s as expected", tst_strstatus(status));
118 } else {
119 tst_res(TFAIL, "Child %s unexpectedly", tst_strstatus(status));
120 }
121
122 }
123
124 static struct tst_test test = {
125 .test = run,
126 .tcnt = ARRAY_SIZE(tcases),
127 .forks_child = 1,
128 };
129