xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/ptrace/ptrace01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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