summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-03-16 16:47:14 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-03-16 16:47:14 -0400
commita9dce6679d736cb3d612af39bab9f31f8db66f9b (patch)
tree7a5e010464d0154fce53aea3c9e3fac2b37c2912 /tools
parentf67e3fb4891287b8248ebb3320f794b9f5e782d4 (diff)
parent575a0ae9744d571f7c6aae4487a05850baae9e1c (diff)
Merge tag 'pidfd-v5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux
Pull pidfd system call from Christian Brauner: "This introduces the ability to use file descriptors from /proc/<pid>/ as stable handles on struct pid. Even if a pid is recycled the handle will not change. For a start these fds can be used to send signals to the processes they refer to. With the ability to use /proc/<pid> fds as stable handles on struct pid we can fix a long-standing issue where after a process has exited its pid can be reused by another process. If a caller sends a signal to a reused pid it will end up signaling the wrong process. With this patchset we enable a variety of use cases. One obvious example is that we can now safely delegate an important part of process management - sending signals - to processes other than the parent of a given process by sending file descriptors around via scm rights and not fearing that the given process will have been recycled in the meantime. It also allows for easy testing whether a given process is still alive or not by sending signal 0 to a pidfd which is quite handy. There has been some interest in this feature e.g. from systems management (systemd, glibc) and container managers. I have requested and gotten comments from glibc to make sure that this syscall is suitable for their needs as well. In the future I expect it to take on most other pid-based signal syscalls. But such features are left for the future once they are needed. This has been sitting in linux-next for quite a while and has not caused any issues. It comes with selftests which verify basic functionality and also test that a recycled pid cannot be signaled via a pidfd. Jon has written about a prior version of this patchset. It should cover the basic functionality since not a lot has changed since then: https://lwn.net/Articles/773459/ The commit message for the syscall itself is extensively documenting the syscall, including it's functionality and extensibility" * tag 'pidfd-v5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux: selftests: add tests for pidfd_send_signal() signal: add pidfd_send_signal() syscall
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 fb5758ac469e..971fc8428117 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -32,6 +32,7 @@ TARGETS += net
32TARGETS += netfilter 32TARGETS += netfilter
33TARGETS += networking/timestamping 33TARGETS += networking/timestamping
34TARGETS += nsfs 34TARGETS += nsfs
35TARGETS += pidfd
35TARGETS += powerpc 36TARGETS += powerpc
36TARGETS += proc 37TARGETS += proc
37TARGETS += pstore 38TARGETS += 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}