summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorChristian Brauner <christian@brauner.io>2018-12-29 16:27:33 -0500
committerChristian Brauner <christian@brauner.io>2019-03-05 11:04:33 -0500
commit575a0ae9744d571f7c6aae4487a05850baae9e1c (patch)
tree1a0a89a61e0db7064fa195b23947e1baa86b63f0 /tools
parent3eb39f47934f9d5a3027fe00d906a45fe3a15fad (diff)
selftests: add tests for pidfd_send_signal()
As suggested by Andrew Morton in [1] add selftests for the new sys_pidfd_send_signal() syscall: /* test_pidfd_send_signal_syscall_support */ Test whether the pidfd_send_signal() syscall is supported and the tests can be run or need to be skipped. /* test_pidfd_send_signal_simple_success */ Test whether sending a signal via a pidfd works. /* test_pidfd_send_signal_exited_fail */ Verify that sending a signal to an already exited process fails with ESRCH. /* test_pidfd_send_signal_recycled_pid_fail */ Verify that a recycled pid cannot be signaled via a pidfd referring to an already exited process that had the same pid (cf. [2], [3]). [1]: https://lore.kernel.org/lkml/20181228152012.dbf0508c2508138efc5f2bbe@linux-foundation.org/ [2]: https://lore.kernel.org/lkml/20181230210245.GA30252@mail.hallyn.com/ [3]: https://lore.kernel.org/lkml/20181230232711.7aayb7vnhogbv4co@brauner.io/ Cc: Arnd Bergmann <arnd@arndb.de> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Kees Cook <keescook@chromium.org> Cc: Jann Horn <jannh@google.com> Cc: Andy Lutomirsky <luto@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Aleksa Sarai <cyphar@cyphar.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Florian Weimer <fweimer@redhat.com> Signed-off-by: Christian Brauner <christian@brauner.io> Reviewed-by: Tycho Andersen <tycho@tycho.ws> Acked-by: Serge Hallyn <serge@hallyn.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/testing/selftests/Makefile1
-rw-r--r--tools/testing/selftests/pidfd/Makefile6
-rw-r--r--tools/testing/selftests/pidfd/pidfd_test.c381
3 files changed, 388 insertions, 0 deletions
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 1a2bd15c5b6e..cfbc3528e9ef 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -30,6 +30,7 @@ TARGETS += net
30TARGETS += netfilter 30TARGETS += netfilter
31TARGETS += networking/timestamping 31TARGETS += networking/timestamping
32TARGETS += nsfs 32TARGETS += nsfs
33TARGETS += pidfd
33TARGETS += powerpc 34TARGETS += powerpc
34TARGETS += proc 35TARGETS += proc
35TARGETS += pstore 36TARGETS += pstore
diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile
new file mode 100644
index 000000000000..deaf8073bc06
--- /dev/null
+++ b/tools/testing/selftests/pidfd/Makefile
@@ -0,0 +1,6 @@
1CFLAGS += -g -I../../../../usr/include/
2
3TEST_GEN_PROGS := pidfd_test
4
5include ../lib.mk
6
diff --git a/tools/testing/selftests/pidfd/pidfd_test.c b/tools/testing/selftests/pidfd/pidfd_test.c
new file mode 100644
index 000000000000..d59378a93782
--- /dev/null
+++ b/tools/testing/selftests/pidfd/pidfd_test.c
@@ -0,0 +1,381 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2
3#define _GNU_SOURCE
4#include <errno.h>
5#include <fcntl.h>
6#include <linux/types.h>
7#include <sched.h>
8#include <signal.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <syscall.h>
13#include <sys/mount.h>
14#include <sys/wait.h>
15#include <unistd.h>
16
17#include "../kselftest.h"
18
19static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
20 unsigned int flags)
21{
22 return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
23}
24
25static int signal_received;
26
27static void set_signal_received_on_sigusr1(int sig)
28{
29 if (sig == SIGUSR1)
30 signal_received = 1;
31}
32
33/*
34 * Straightforward test to see whether pidfd_send_signal() works is to send
35 * a signal to ourself.
36 */
37static int test_pidfd_send_signal_simple_success(void)
38{
39 int pidfd, ret;
40 const char *test_name = "pidfd_send_signal send SIGUSR1";
41
42 pidfd = open("/proc/self", O_DIRECTORY | O_CLOEXEC);
43 if (pidfd < 0)
44 ksft_exit_fail_msg(
45 "%s test: Failed to open process file descriptor\n",
46 test_name);
47
48 signal(SIGUSR1, set_signal_received_on_sigusr1);
49
50 ret = sys_pidfd_send_signal(pidfd, SIGUSR1, NULL, 0);
51 close(pidfd);
52 if (ret < 0)
53 ksft_exit_fail_msg("%s test: Failed to send signal\n",
54 test_name);
55
56 if (signal_received != 1)
57 ksft_exit_fail_msg("%s test: Failed to receive signal\n",
58 test_name);
59
60 signal_received = 0;
61 ksft_test_result_pass("%s test: Sent signal\n", test_name);
62 return 0;
63}
64
65static int wait_for_pid(pid_t pid)
66{
67 int status, ret;
68
69again:
70 ret = waitpid(pid, &status, 0);
71 if (ret == -1) {
72 if (errno == EINTR)
73 goto again;
74
75 return -1;
76 }
77
78 if (ret != pid)
79 goto again;
80
81 if (!WIFEXITED(status))
82 return -1;
83
84 return WEXITSTATUS(status);
85}
86
87static int test_pidfd_send_signal_exited_fail(void)
88{
89 int pidfd, ret, saved_errno;
90 char buf[256];
91 pid_t pid;
92 const char *test_name = "pidfd_send_signal signal exited process";
93
94 pid = fork();
95 if (pid < 0)
96 ksft_exit_fail_msg("%s test: Failed to create new process\n",
97 test_name);
98
99 if (pid == 0)
100 _exit(EXIT_SUCCESS);
101
102 snprintf(buf, sizeof(buf), "/proc/%d", pid);
103
104 pidfd = open(buf, O_DIRECTORY | O_CLOEXEC);
105
106 (void)wait_for_pid(pid);
107
108 if (pidfd < 0)
109 ksft_exit_fail_msg(
110 "%s test: Failed to open process file descriptor\n",
111 test_name);
112
113 ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0);
114 saved_errno = errno;
115 close(pidfd);
116 if (ret == 0)
117 ksft_exit_fail_msg(
118 "%s test: Managed to send signal to process even though it should have failed\n",
119 test_name);
120
121 if (saved_errno != ESRCH)
122 ksft_exit_fail_msg(
123 "%s test: Expected to receive ESRCH as errno value but received %d instead\n",
124 test_name, saved_errno);
125
126 ksft_test_result_pass("%s test: Failed to send signal as expected\n",
127 test_name);
128 return 0;
129}
130
131/*
132 * The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c
133 * That means, when it wraps around any pid < 300 will be skipped.
134 * So we need to use a pid > 300 in order to test recycling.
135 */
136#define PID_RECYCLE 1000
137
138/*
139 * Maximum number of cycles we allow. This is equivalent to PID_MAX_DEFAULT.
140 * If users set a higher limit or we have cycled PIDFD_MAX_DEFAULT number of
141 * times then we skip the test to not go into an infinite loop or block for a
142 * long time.
143 */
144#define PIDFD_MAX_DEFAULT 0x8000
145
146/*
147 * Define a few custom error codes for the child process to clearly indicate
148 * what is happening. This way we can tell the difference between a system
149 * error, a test error, etc.
150 */
151#define PIDFD_PASS 0
152#define PIDFD_FAIL 1
153#define PIDFD_ERROR 2
154#define PIDFD_SKIP 3
155#define PIDFD_XFAIL 4
156
157static int test_pidfd_send_signal_recycled_pid_fail(void)
158{
159 int i, ret;
160 pid_t pid1;
161 const char *test_name = "pidfd_send_signal signal recycled pid";
162
163 ret = unshare(CLONE_NEWPID);
164 if (ret < 0)
165 ksft_exit_fail_msg("%s test: Failed to unshare pid namespace\n",
166 test_name);
167
168 ret = unshare(CLONE_NEWNS);
169 if (ret < 0)
170 ksft_exit_fail_msg(
171 "%s test: Failed to unshare mount namespace\n",
172 test_name);
173
174 ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
175 if (ret < 0)
176 ksft_exit_fail_msg("%s test: Failed to remount / private\n",
177 test_name);
178
179 /* pid 1 in new pid namespace */
180 pid1 = fork();
181 if (pid1 < 0)
182 ksft_exit_fail_msg("%s test: Failed to create new process\n",
183 test_name);
184
185 if (pid1 == 0) {
186 char buf[256];
187 pid_t pid2;
188 int pidfd = -1;
189
190 (void)umount2("/proc", MNT_DETACH);
191 ret = mount("proc", "/proc", "proc", 0, NULL);
192 if (ret < 0)
193 _exit(PIDFD_ERROR);
194
195 /* grab pid PID_RECYCLE */
196 for (i = 0; i <= PIDFD_MAX_DEFAULT; i++) {
197 pid2 = fork();
198 if (pid2 < 0)
199 _exit(PIDFD_ERROR);
200
201 if (pid2 == 0)
202 _exit(PIDFD_PASS);
203
204 if (pid2 == PID_RECYCLE) {
205 snprintf(buf, sizeof(buf), "/proc/%d", pid2);
206 ksft_print_msg("pid to recycle is %d\n", pid2);
207 pidfd = open(buf, O_DIRECTORY | O_CLOEXEC);
208 }
209
210 if (wait_for_pid(pid2))
211 _exit(PIDFD_ERROR);
212
213 if (pid2 >= PID_RECYCLE)
214 break;
215 }
216
217 /*
218 * We want to be as predictable as we can so if we haven't been
219 * able to grab pid PID_RECYCLE skip the test.
220 */
221 if (pid2 != PID_RECYCLE) {
222 /* skip test */
223 close(pidfd);
224 _exit(PIDFD_SKIP);
225 }
226
227 if (pidfd < 0)
228 _exit(PIDFD_ERROR);
229
230 for (i = 0; i <= PIDFD_MAX_DEFAULT; i++) {
231 char c;
232 int pipe_fds[2];
233 pid_t recycled_pid;
234 int child_ret = PIDFD_PASS;
235
236 ret = pipe2(pipe_fds, O_CLOEXEC);
237 if (ret < 0)
238 _exit(PIDFD_ERROR);
239
240 recycled_pid = fork();
241 if (recycled_pid < 0)
242 _exit(PIDFD_ERROR);
243
244 if (recycled_pid == 0) {
245 close(pipe_fds[1]);
246 (void)read(pipe_fds[0], &c, 1);
247 close(pipe_fds[0]);
248
249 _exit(PIDFD_PASS);
250 }
251
252 /*
253 * Stop the child so we can inspect whether we have
254 * recycled pid PID_RECYCLE.
255 */
256 close(pipe_fds[0]);
257 ret = kill(recycled_pid, SIGSTOP);
258 close(pipe_fds[1]);
259 if (ret) {
260 (void)wait_for_pid(recycled_pid);
261 _exit(PIDFD_ERROR);
262 }
263
264 /*
265 * We have recycled the pid. Try to signal it. This
266 * needs to fail since this is a different process than
267 * the one the pidfd refers to.
268 */
269 if (recycled_pid == PID_RECYCLE) {
270 ret = sys_pidfd_send_signal(pidfd, SIGCONT,
271 NULL, 0);
272 if (ret && errno == ESRCH)
273 child_ret = PIDFD_XFAIL;
274 else
275 child_ret = PIDFD_FAIL;
276 }
277
278 /* let the process move on */
279 ret = kill(recycled_pid, SIGCONT);
280 if (ret)
281 (void)kill(recycled_pid, SIGKILL);
282
283 if (wait_for_pid(recycled_pid))
284 _exit(PIDFD_ERROR);
285
286 switch (child_ret) {
287 case PIDFD_FAIL:
288 /* fallthrough */
289 case PIDFD_XFAIL:
290 _exit(child_ret);
291 case PIDFD_PASS:
292 break;
293 default:
294 /* not reached */
295 _exit(PIDFD_ERROR);
296 }
297
298 /*
299 * If the user set a custom pid_max limit we could be
300 * in the millions.
301 * Skip the test in this case.
302 */
303 if (recycled_pid > PIDFD_MAX_DEFAULT)
304 _exit(PIDFD_SKIP);
305 }
306
307 /* failed to recycle pid */
308 _exit(PIDFD_SKIP);
309 }
310
311 ret = wait_for_pid(pid1);
312 switch (ret) {
313 case PIDFD_FAIL:
314 ksft_exit_fail_msg(
315 "%s test: Managed to signal recycled pid %d\n",
316 test_name, PID_RECYCLE);
317 case PIDFD_PASS:
318 ksft_exit_fail_msg("%s test: Failed to recycle pid %d\n",
319 test_name, PID_RECYCLE);
320 case PIDFD_SKIP:
321 ksft_print_msg("%s test: Skipping test\n", test_name);
322 ret = 0;
323 break;
324 case PIDFD_XFAIL:
325 ksft_test_result_pass(
326 "%s test: Failed to signal recycled pid as expected\n",
327 test_name);
328 ret = 0;
329 break;
330 default /* PIDFD_ERROR */:
331 ksft_exit_fail_msg("%s test: Error while running tests\n",
332 test_name);
333 }
334
335 return ret;
336}
337
338static int test_pidfd_send_signal_syscall_support(void)
339{
340 int pidfd, ret;
341 const char *test_name = "pidfd_send_signal check for support";
342
343 pidfd = open("/proc/self", O_DIRECTORY | O_CLOEXEC);
344 if (pidfd < 0)
345 ksft_exit_fail_msg(
346 "%s test: Failed to open process file descriptor\n",
347 test_name);
348
349 ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0);
350 if (ret < 0) {
351 /*
352 * pidfd_send_signal() will currently return ENOSYS when
353 * CONFIG_PROC_FS is not set.
354 */
355 if (errno == ENOSYS)
356 ksft_exit_skip(
357 "%s test: pidfd_send_signal() syscall not supported (Ensure that CONFIG_PROC_FS=y is set)\n",
358 test_name);
359
360 ksft_exit_fail_msg("%s test: Failed to send signal\n",
361 test_name);
362 }
363
364 close(pidfd);
365 ksft_test_result_pass(
366 "%s test: pidfd_send_signal() syscall is supported. Tests can be executed\n",
367 test_name);
368 return 0;
369}
370
371int main(int argc, char **argv)
372{
373 ksft_print_header();
374
375 test_pidfd_send_signal_syscall_support();
376 test_pidfd_send_signal_simple_success();
377 test_pidfd_send_signal_exited_fail();
378 test_pidfd_send_signal_recycled_pid_fail();
379
380 return ksft_exit_pass();
381}