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_harness.h" |
21 | |
22 | #define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr))) |
23 | |
24 | /* Attempt to de-conflict with the selftests tree. */ |
25 | #ifndef SKIP |
26 | #define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__) |
27 | #endif |
28 | |
29 | static pid_t sys_clone3(struct clone_args *args) |
30 | { |
31 | return syscall(__NR_clone3, args, sizeof(struct clone_args)); |
32 | } |
33 | |
34 | static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options, |
35 | struct rusage *ru) |
36 | { |
37 | return syscall(__NR_waitid, which, pid, info, options, ru); |
38 | } |
39 | |
40 | TEST(wait_simple) |
41 | { |
42 | int pidfd = -1; |
43 | pid_t parent_tid = -1; |
44 | struct clone_args args = { |
45 | .parent_tid = ptr_to_u64(&parent_tid), |
46 | .pidfd = ptr_to_u64(&pidfd), |
47 | .flags = CLONE_PIDFD | CLONE_PARENT_SETTID, |
48 | .exit_signal = SIGCHLD, |
49 | }; |
50 | pid_t pid; |
51 | siginfo_t info = { |
52 | .si_signo = 0, |
53 | }; |
54 | |
55 | pidfd = open("/proc/self" , O_DIRECTORY | O_RDONLY | O_CLOEXEC); |
56 | ASSERT_GE(pidfd, 0); |
57 | |
58 | pid = sys_waitid(P_PIDFD, pid: pidfd, info: &info, options: WEXITED, NULL); |
59 | ASSERT_NE(pid, 0); |
60 | EXPECT_EQ(close(pidfd), 0); |
61 | pidfd = -1; |
62 | |
63 | pidfd = open("/dev/null" , O_RDONLY | O_CLOEXEC); |
64 | ASSERT_GE(pidfd, 0); |
65 | |
66 | pid = sys_waitid(P_PIDFD, pid: pidfd, info: &info, options: WEXITED, NULL); |
67 | ASSERT_NE(pid, 0); |
68 | EXPECT_EQ(close(pidfd), 0); |
69 | pidfd = -1; |
70 | |
71 | pid = sys_clone3(args: &args); |
72 | ASSERT_GE(pid, 0); |
73 | |
74 | if (pid == 0) |
75 | exit(EXIT_SUCCESS); |
76 | |
77 | pid = sys_waitid(P_PIDFD, pid: pidfd, info: &info, options: WEXITED, NULL); |
78 | ASSERT_GE(pid, 0); |
79 | ASSERT_EQ(WIFEXITED(info.si_status), true); |
80 | ASSERT_EQ(WEXITSTATUS(info.si_status), 0); |
81 | EXPECT_EQ(close(pidfd), 0); |
82 | |
83 | ASSERT_EQ(info.si_signo, SIGCHLD); |
84 | ASSERT_EQ(info.si_code, CLD_EXITED); |
85 | ASSERT_EQ(info.si_pid, parent_tid); |
86 | } |
87 | |
88 | TEST(wait_states) |
89 | { |
90 | int pidfd = -1; |
91 | pid_t parent_tid = -1; |
92 | struct clone_args args = { |
93 | .parent_tid = ptr_to_u64(&parent_tid), |
94 | .pidfd = ptr_to_u64(&pidfd), |
95 | .flags = CLONE_PIDFD | CLONE_PARENT_SETTID, |
96 | .exit_signal = SIGCHLD, |
97 | }; |
98 | int pfd[2]; |
99 | pid_t pid; |
100 | siginfo_t info = { |
101 | .si_signo = 0, |
102 | }; |
103 | |
104 | ASSERT_EQ(pipe(pfd), 0); |
105 | pid = sys_clone3(args: &args); |
106 | ASSERT_GE(pid, 0); |
107 | |
108 | if (pid == 0) { |
109 | char buf[2]; |
110 | |
111 | close(pfd[1]); |
112 | kill(getpid(), SIGSTOP); |
113 | ASSERT_EQ(read(pfd[0], buf, 1), 1); |
114 | close(pfd[0]); |
115 | kill(getpid(), SIGSTOP); |
116 | exit(EXIT_SUCCESS); |
117 | } |
118 | |
119 | close(pfd[0]); |
120 | ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0); |
121 | ASSERT_EQ(info.si_signo, SIGCHLD); |
122 | ASSERT_EQ(info.si_code, CLD_STOPPED); |
123 | ASSERT_EQ(info.si_pid, parent_tid); |
124 | |
125 | ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0); |
126 | |
127 | ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL), 0); |
128 | ASSERT_EQ(write(pfd[1], "C" , 1), 1); |
129 | close(pfd[1]); |
130 | ASSERT_EQ(info.si_signo, SIGCHLD); |
131 | ASSERT_EQ(info.si_code, CLD_CONTINUED); |
132 | ASSERT_EQ(info.si_pid, parent_tid); |
133 | |
134 | ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL), 0); |
135 | ASSERT_EQ(info.si_signo, SIGCHLD); |
136 | ASSERT_EQ(info.si_code, CLD_STOPPED); |
137 | ASSERT_EQ(info.si_pid, parent_tid); |
138 | |
139 | ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0), 0); |
140 | |
141 | ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0); |
142 | ASSERT_EQ(info.si_signo, SIGCHLD); |
143 | ASSERT_EQ(info.si_code, CLD_KILLED); |
144 | ASSERT_EQ(info.si_pid, parent_tid); |
145 | |
146 | EXPECT_EQ(close(pidfd), 0); |
147 | } |
148 | |
149 | TEST(wait_nonblock) |
150 | { |
151 | int pidfd; |
152 | unsigned int flags = 0; |
153 | pid_t parent_tid = -1; |
154 | struct clone_args args = { |
155 | .parent_tid = ptr_to_u64(&parent_tid), |
156 | .flags = CLONE_PARENT_SETTID, |
157 | .exit_signal = SIGCHLD, |
158 | }; |
159 | int ret; |
160 | pid_t pid; |
161 | siginfo_t info = { |
162 | .si_signo = 0, |
163 | }; |
164 | |
165 | /* |
166 | * Callers need to see ECHILD with non-blocking pidfds when no child |
167 | * processes exists. |
168 | */ |
169 | pidfd = sys_pidfd_open(getpid(), PIDFD_NONBLOCK); |
170 | EXPECT_GE(pidfd, 0) { |
171 | /* pidfd_open() doesn't support PIDFD_NONBLOCK. */ |
172 | ASSERT_EQ(errno, EINVAL); |
173 | SKIP(return, "Skipping PIDFD_NONBLOCK test" ); |
174 | } |
175 | |
176 | ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); |
177 | ASSERT_LT(ret, 0); |
178 | ASSERT_EQ(errno, ECHILD); |
179 | EXPECT_EQ(close(pidfd), 0); |
180 | |
181 | pid = sys_clone3(args: &args); |
182 | ASSERT_GE(pid, 0); |
183 | |
184 | if (pid == 0) { |
185 | kill(getpid(), SIGSTOP); |
186 | exit(EXIT_SUCCESS); |
187 | } |
188 | |
189 | pidfd = sys_pidfd_open(pid, PIDFD_NONBLOCK); |
190 | EXPECT_GE(pidfd, 0) { |
191 | /* pidfd_open() doesn't support PIDFD_NONBLOCK. */ |
192 | ASSERT_EQ(errno, EINVAL); |
193 | SKIP(return, "Skipping PIDFD_NONBLOCK test" ); |
194 | } |
195 | |
196 | flags = fcntl(pidfd, F_GETFL, 0); |
197 | ASSERT_GT(flags, 0); |
198 | ASSERT_GT((flags & O_NONBLOCK), 0); |
199 | |
200 | /* |
201 | * Callers need to see EAGAIN/EWOULDBLOCK with non-blocking pidfd when |
202 | * child processes exist but none have exited. |
203 | */ |
204 | ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); |
205 | ASSERT_LT(ret, 0); |
206 | ASSERT_EQ(errno, EAGAIN); |
207 | |
208 | /* |
209 | * Callers need to continue seeing 0 with non-blocking pidfd and |
210 | * WNOHANG raised explicitly when child processes exist but none have |
211 | * exited. |
212 | */ |
213 | ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED | WNOHANG, NULL); |
214 | ASSERT_EQ(ret, 0); |
215 | |
216 | ASSERT_EQ(fcntl(pidfd, F_SETFL, (flags & ~O_NONBLOCK)), 0); |
217 | |
218 | ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0); |
219 | ASSERT_EQ(info.si_signo, SIGCHLD); |
220 | ASSERT_EQ(info.si_code, CLD_STOPPED); |
221 | ASSERT_EQ(info.si_pid, parent_tid); |
222 | |
223 | ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0); |
224 | |
225 | ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0); |
226 | ASSERT_EQ(info.si_signo, SIGCHLD); |
227 | ASSERT_EQ(info.si_code, CLD_EXITED); |
228 | ASSERT_EQ(info.si_pid, parent_tid); |
229 | |
230 | EXPECT_EQ(close(pidfd), 0); |
231 | } |
232 | |
233 | TEST_HARNESS_MAIN |
234 | |