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