1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 SUSE LLC
4 * Author: Christian Amann <camann@suse.com>
5 */
6 /*
7 * This test checks if the pidfd_send_signal syscall wrongfully sends
8 * a signal to a new process which inherited the PID of the actual
9 * target process.
10 * In order to do so it is necessary to start a process with a pre-
11 * determined PID. This is accomplished by writing to the
12 * /proc/sys/kernel/ns_last_pid file.
13 * By utilizing this, this test forks two children with the same PID.
14 * It is then checked, if the syscall will send a signal to the second
15 * child using the pidfd of the first one.
16 */
17
18 #define _GNU_SOURCE
19 #include <signal.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include "lapi/pidfd_send_signal.h"
23 #include "tst_safe_pthread.h"
24
25 #define PIDTRIES 3
26
27 static char *last_pid_file;
28 static int pidfd, new_pidfd;
29 static int old_inode, new_inode;
30
get_inode_number(int fd)31 static int get_inode_number(int fd)
32 {
33 struct stat file_stat;
34
35 SAFE_FSTAT(fd, &file_stat);
36 return file_stat.st_ino;
37 }
38
verify_pidfd_send_signal(void)39 static void verify_pidfd_send_signal(void)
40 {
41 pid_t pid, new_pid;
42 char pid_filename[32];
43 char pid_str[16];
44 int i, fail;
45
46 fail = 1;
47 for (i = 0; i < PIDTRIES; i++) {
48 pid = SAFE_FORK();
49 if (pid == 0) {
50 TST_CHECKPOINT_WAIT(0);
51 return;
52 }
53
54 sprintf(pid_filename, "/proc/%d", pid);
55 pidfd = SAFE_OPEN(pid_filename, O_DIRECTORY | O_CLOEXEC);
56 old_inode = get_inode_number(pidfd);
57
58 TST_CHECKPOINT_WAKE(0);
59 tst_reap_children();
60
61 /* Manipulate PID for next process */
62 sprintf(pid_str, "%d", pid - 1);
63 SAFE_FILE_PRINTF(last_pid_file, "%s", pid_str);
64
65 new_pid = SAFE_FORK();
66 if (new_pid == 0) {
67 TST_CHECKPOINT_WAIT(0);
68 return;
69 }
70
71 if (new_pid == pid) {
72 new_pidfd = SAFE_OPEN(pid_filename,
73 O_DIRECTORY | O_CLOEXEC);
74 new_inode = get_inode_number(new_pidfd);
75 SAFE_CLOSE(new_pidfd);
76 fail = 0;
77 break;
78 }
79
80 if (i < PIDTRIES) {
81 tst_res(TINFO,
82 "Failed to set correct PID, trying again...");
83 }
84 SAFE_CLOSE(pidfd);
85 TST_CHECKPOINT_WAKE(0);
86 tst_reap_children();
87 }
88 if (fail) {
89 tst_brk(TBROK,
90 "Could not set new child to same PID as the old one!");
91 }
92 if (old_inode == new_inode) {
93 tst_res(TWARN,
94 "File descriptor of new process points to the inode of the old process!");
95 }
96
97 TEST(pidfd_send_signal(pidfd, SIGUSR1, NULL, 0));
98 if (TST_RET == -1 && TST_ERR == ESRCH) {
99 tst_res(TPASS,
100 "Did not send signal to wrong process with same PID!");
101 } else {
102 tst_res(TFAIL | TTERRNO,
103 "pidf_send_signal() ended unexpectedly - return value: %ld, error",
104 TST_RET);
105 }
106 TST_CHECKPOINT_WAKE(0);
107 tst_reap_children();
108
109 SAFE_CLOSE(pidfd);
110 }
111
setup(void)112 static void setup(void)
113 {
114 pidfd_send_signal_supported();
115
116 last_pid_file = "/proc/sys/kernel/ns_last_pid";
117 if (access(last_pid_file, F_OK) == -1) {
118 tst_brk(TCONF, "%s does not exist, cannot set PIDs",
119 last_pid_file);
120 }
121 }
122
cleanup(void)123 static void cleanup(void)
124 {
125 tst_reap_children();
126 if (new_pidfd > 0)
127 SAFE_CLOSE(new_pidfd);
128 if (pidfd > 0)
129 SAFE_CLOSE(pidfd);
130 }
131
132 static struct tst_test test = {
133 .test_all = verify_pidfd_send_signal,
134 .setup = setup,
135 .cleanup = cleanup,
136 .needs_root = 1,
137 .needs_checkpoints = 1,
138 .forks_child = 1,
139 };
140