xref: /linux/tools/testing/selftests/pidfd/pidfd_wait.c (revision e5a52fd2b8cdb700b3c07b030e050a49ef3156b9)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 
3 #define _GNU_SOURCE
4 #include <errno.h>
5 #include <linux/sched.h>
6 #include <linux/types.h>
7 #include <signal.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <sched.h>
12 #include <string.h>
13 #include <sys/resource.h>
14 #include <sys/time.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18 
19 #include "pidfd.h"
20 #include "../kselftest.h"
21 
22 #define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
23 
24 static pid_t sys_clone3(struct clone_args *args)
25 {
26 	return syscall(__NR_clone3, args, sizeof(struct clone_args));
27 }
28 
29 static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options,
30 		      struct rusage *ru)
31 {
32 	return syscall(__NR_waitid, which, pid, info, options, ru);
33 }
34 
35 static int test_pidfd_wait_simple(void)
36 {
37 	const char *test_name = "pidfd wait simple";
38 	int pidfd = -1, status = 0;
39 	pid_t parent_tid = -1;
40 	struct clone_args args = {
41 		.parent_tid = ptr_to_u64(&parent_tid),
42 		.pidfd = ptr_to_u64(&pidfd),
43 		.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
44 		.exit_signal = SIGCHLD,
45 	};
46 	int ret;
47 	pid_t pid;
48 	siginfo_t info = {
49 		.si_signo = 0,
50 	};
51 
52 	pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
53 	if (pidfd < 0)
54 		ksft_exit_fail_msg("%s test: failed to open /proc/self %s\n",
55 				   test_name, strerror(errno));
56 
57 	pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
58 	if (pid == 0)
59 		ksft_exit_fail_msg(
60 			"%s test: succeeded to wait on invalid pidfd %s\n",
61 			test_name, strerror(errno));
62 	close(pidfd);
63 	pidfd = -1;
64 
65 	pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC);
66 	if (pidfd == 0)
67 		ksft_exit_fail_msg("%s test: failed to open /dev/null %s\n",
68 				   test_name, strerror(errno));
69 
70 	pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
71 	if (pid == 0)
72 		ksft_exit_fail_msg(
73 			"%s test: succeeded to wait on invalid pidfd %s\n",
74 			test_name, strerror(errno));
75 	close(pidfd);
76 	pidfd = -1;
77 
78 	pid = sys_clone3(&args);
79 	if (pid < 0)
80 		ksft_exit_fail_msg("%s test: failed to create new process %s\n",
81 				   test_name, strerror(errno));
82 
83 	if (pid == 0)
84 		exit(EXIT_SUCCESS);
85 
86 	pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
87 	if (pid < 0)
88 		ksft_exit_fail_msg(
89 			"%s test: failed to wait on process with pid %d and pidfd %d: %s\n",
90 			test_name, parent_tid, pidfd, strerror(errno));
91 
92 	if (!WIFEXITED(info.si_status) || WEXITSTATUS(info.si_status))
93 		ksft_exit_fail_msg(
94 			"%s test: unexpected status received after waiting on process with pid %d and pidfd %d: %s\n",
95 			test_name, parent_tid, pidfd, strerror(errno));
96 	close(pidfd);
97 
98 	if (info.si_signo != SIGCHLD)
99 		ksft_exit_fail_msg(
100 			"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
101 			test_name, info.si_signo, parent_tid, pidfd,
102 			strerror(errno));
103 
104 	if (info.si_code != CLD_EXITED)
105 		ksft_exit_fail_msg(
106 			"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
107 			test_name, info.si_code, parent_tid, pidfd,
108 			strerror(errno));
109 
110 	if (info.si_pid != parent_tid)
111 		ksft_exit_fail_msg(
112 			"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
113 			test_name, info.si_pid, parent_tid, pidfd,
114 			strerror(errno));
115 
116 	ksft_test_result_pass("%s test: Passed\n", test_name);
117 	return 0;
118 }
119 
120 static int test_pidfd_wait_states(void)
121 {
122 	const char *test_name = "pidfd wait states";
123 	int pidfd = -1, status = 0;
124 	pid_t parent_tid = -1;
125 	struct clone_args args = {
126 		.parent_tid = ptr_to_u64(&parent_tid),
127 		.pidfd = ptr_to_u64(&pidfd),
128 		.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
129 		.exit_signal = SIGCHLD,
130 	};
131 	int ret;
132 	pid_t pid;
133 	siginfo_t info = {
134 		.si_signo = 0,
135 	};
136 
137 	pid = sys_clone3(&args);
138 	if (pid < 0)
139 		ksft_exit_fail_msg("%s test: failed to create new process %s\n",
140 				   test_name, strerror(errno));
141 
142 	if (pid == 0) {
143 		kill(getpid(), SIGSTOP);
144 		kill(getpid(), SIGSTOP);
145 		exit(EXIT_SUCCESS);
146 	}
147 
148 	ret = sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL);
149 	if (ret < 0)
150 		ksft_exit_fail_msg(
151 			"%s test: failed to wait on WSTOPPED process with pid %d and pidfd %d: %s\n",
152 			test_name, parent_tid, pidfd, strerror(errno));
153 
154 	if (info.si_signo != SIGCHLD)
155 		ksft_exit_fail_msg(
156 			"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
157 			test_name, info.si_signo, parent_tid, pidfd,
158 			strerror(errno));
159 
160 	if (info.si_code != CLD_STOPPED)
161 		ksft_exit_fail_msg(
162 			"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
163 			test_name, info.si_code, parent_tid, pidfd,
164 			strerror(errno));
165 
166 	if (info.si_pid != parent_tid)
167 		ksft_exit_fail_msg(
168 			"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
169 			test_name, info.si_pid, parent_tid, pidfd,
170 			strerror(errno));
171 
172 	ret = sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0);
173 	if (ret < 0)
174 		ksft_exit_fail_msg(
175 			"%s test: failed to send signal to process with pid %d and pidfd %d: %s\n",
176 			test_name, parent_tid, pidfd, strerror(errno));
177 
178 	ret = sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL);
179 	if (ret < 0)
180 		ksft_exit_fail_msg(
181 			"%s test: failed to wait WCONTINUED on process with pid %d and pidfd %d: %s\n",
182 			test_name, parent_tid, pidfd, strerror(errno));
183 
184 	if (info.si_signo != SIGCHLD)
185 		ksft_exit_fail_msg(
186 			"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
187 			test_name, info.si_signo, parent_tid, pidfd,
188 			strerror(errno));
189 
190 	if (info.si_code != CLD_CONTINUED)
191 		ksft_exit_fail_msg(
192 			"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
193 			test_name, info.si_code, parent_tid, pidfd,
194 			strerror(errno));
195 
196 	if (info.si_pid != parent_tid)
197 		ksft_exit_fail_msg(
198 			"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
199 			test_name, info.si_pid, parent_tid, pidfd,
200 			strerror(errno));
201 
202 	ret = sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL);
203 	if (ret < 0)
204 		ksft_exit_fail_msg(
205 			"%s test: failed to wait on WUNTRACED process with pid %d and pidfd %d: %s\n",
206 			test_name, parent_tid, pidfd, strerror(errno));
207 
208 	if (info.si_signo != SIGCHLD)
209 		ksft_exit_fail_msg(
210 			"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
211 			test_name, info.si_signo, parent_tid, pidfd,
212 			strerror(errno));
213 
214 	if (info.si_code != CLD_STOPPED)
215 		ksft_exit_fail_msg(
216 			"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
217 			test_name, info.si_code, parent_tid, pidfd,
218 			strerror(errno));
219 
220 	if (info.si_pid != parent_tid)
221 		ksft_exit_fail_msg(
222 			"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
223 			test_name, info.si_pid, parent_tid, pidfd,
224 			strerror(errno));
225 
226 	ret = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
227 	if (ret < 0)
228 		ksft_exit_fail_msg(
229 			"%s test: failed to send SIGKILL to process with pid %d and pidfd %d: %s\n",
230 			test_name, parent_tid, pidfd, strerror(errno));
231 
232 	ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
233 	if (ret < 0)
234 		ksft_exit_fail_msg(
235 			"%s test: failed to wait on WEXITED process with pid %d and pidfd %d: %s\n",
236 			test_name, parent_tid, pidfd, strerror(errno));
237 
238 	if (info.si_signo != SIGCHLD)
239 		ksft_exit_fail_msg(
240 			"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
241 			test_name, info.si_signo, parent_tid, pidfd,
242 			strerror(errno));
243 
244 	if (info.si_code != CLD_KILLED)
245 		ksft_exit_fail_msg(
246 			"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
247 			test_name, info.si_code, parent_tid, pidfd,
248 			strerror(errno));
249 
250 	if (info.si_pid != parent_tid)
251 		ksft_exit_fail_msg(
252 			"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
253 			test_name, info.si_pid, parent_tid, pidfd,
254 			strerror(errno));
255 
256 	close(pidfd);
257 
258 	ksft_test_result_pass("%s test: Passed\n", test_name);
259 	return 0;
260 }
261 
262 int main(int argc, char **argv)
263 {
264 	ksft_print_header();
265 	ksft_set_plan(2);
266 
267 	test_pidfd_wait_simple();
268 	test_pidfd_wait_states();
269 
270 	return ksft_exit_pass();
271 }
272