1 /*
2 * Copyright (C) 2013-2021 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * This code is a complete clean re-write of the stress tool by
19 * Colin Ian King <colin.king@canonical.com> and attempts to be
20 * backwardly compatible with the stress tool by Amos Waterland
21 * <apw@rossby.metr.ou.edu> but has more stress tests and more
22 * functionality.
23 *
24 */
25 #include "stress-ng.h"
26
27 static const stress_help_t help[] = {
28 { NULL, "pidfd N", "start N workers exercising pidfd system call" },
29 { NULL, "pidfd-ops N", "stop after N pidfd bogo operations" },
30 { NULL, NULL, NULL }
31 };
32
33 #if defined(HAVE_PIDFD_SEND_SIGNAL)
34
stress_pidfd_open(pid_t pid,unsigned int flag)35 static int stress_pidfd_open(pid_t pid, unsigned int flag)
36 {
37 int fd;
38 const pid_t bad_pid = stress_get_unused_pid_racy(false);
39
40 /* Exercise pidfd_open with non-existent PID */
41 fd = shim_pidfd_open(bad_pid, 0);
42 if (fd >= 0)
43 (void)close(fd);
44
45 /* Exercise pidfd_open with illegal flags */
46 (void)shim_pidfd_open(pid, ~(1U));
47
48 /* Exercise pidfd_open with illegal PID */
49 (void)shim_pidfd_open((pid_t)-1, 0);
50
51 fd = -1;
52 /* Randomly try pidfd_open first */
53 if (stress_mwc1()) {
54 fd = shim_pidfd_open(pid, flag);
55 }
56 /* ..or fallback to open on /proc/$PID */
57 if (fd < 0) {
58 char buffer[1024];
59 int o_flags = O_DIRECTORY | O_CLOEXEC;
60
61 (void)snprintf(buffer, sizeof(buffer), "/proc/%" PRIdMAX, (intmax_t)pid);
62 #if defined(PIDFD_NONBLOCK)
63 if (flag & PIDFD_NONBLOCK)
64 o_flags |= O_NONBLOCK;
65 #endif
66 fd = open(buffer, o_flags);
67 }
68 return fd;
69 }
70
stress_pidfd_supported(const char * name)71 static int stress_pidfd_supported(const char *name)
72 {
73 int pidfd, ret;
74 const pid_t pid = getpid();
75 siginfo_t info;
76
77 pidfd = stress_pidfd_open(pid, 0);
78 if (pidfd < 0) {
79 pr_inf_skip("%s stressor will be skipped, cannot open proc entry on procfs\n",
80 name);
81 return -1;
82 }
83 ret = shim_pidfd_send_signal(pidfd, 0, NULL, 0);
84 if (ret < 0) {
85 if (errno == ENOSYS) {
86 pr_inf_skip("pidfd stressor will be skipped, system call not implemented\n");
87 (void)close(pidfd);
88 return -1;
89 }
90 /* Something went wrong, but don't let stressor fail on that */
91 }
92
93 /* initialized info to be safe */
94 (void)memset(&info, 0, sizeof(info));
95
96 /*
97 * Exercise pidfd_send_signal with
98 * non-null pointer to info variable
99 */
100 (void)shim_pidfd_send_signal(pidfd, 0, &info, 0);
101
102 /* Exercise pidfd_send_signal with illegal flags */
103 (void)shim_pidfd_send_signal(pidfd, 0, NULL, ~(1U));
104
105 (void)close(pidfd);
106 return 0;
107 }
108
stress_pidfd_reap(pid_t pid,int pidfd)109 static void stress_pidfd_reap(pid_t pid, int pidfd)
110 {
111 int status;
112
113 if (pid) {
114 (void)kill(pid, SIGKILL);
115 (void)shim_process_mrelease(pidfd, 0);
116 (void)shim_waitpid(pid, &status, 0);
117 }
118 if (pidfd >= 0)
119 (void)close(pidfd);
120 }
121
122 /*
123 * stress_pidfd
124 * stress signalfd reads
125 */
stress_pidfd(const stress_args_t * args)126 static int stress_pidfd(const stress_args_t *args)
127 {
128 const int bad_fd = stress_get_bad_fd();
129
130 stress_set_proc_state(args->name, STRESS_STATE_RUN);
131
132 while (keep_stressing(args)) {
133 pid_t pid;
134
135 again:
136 pid = fork();
137 if (pid < 0) {
138 if (stress_redo_fork(errno))
139 goto again;
140 if (!keep_stressing(args))
141 goto finish;
142 pr_fail("%s: fork failed, errno=%d (%s)\n",
143 args->name, errno, strerror(errno));
144 return EXIT_FAILURE;
145 } else if (pid == 0) {
146 (void)setpgid(0, g_pgrp);
147 (void)pause();
148 _exit(0);
149 } else {
150 /* Parent */
151 int pidfd, ret;
152 struct stat statbuf;
153 void *ptr;
154
155 #if defined(PIDFD_NONBLOCK)
156 pidfd = stress_pidfd_open(pid, PIDFD_NONBLOCK);
157 if (pidfd >= 0) {
158 #if defined(F_GETFL) && 0
159 unsigned int flags;
160
161 flags = fcntl(pidfd, F_GETFL, 0);
162 if ((flags & O_NONBLOCK) == 0)
163 pr_fail("%s: pidfd_open opened using PIDFD_NONBLOCK "
164 "but O_NONBLOCK is not set on the file\n",
165 args->name);
166 #endif
167 (void)close(pidfd);
168 }
169 #endif
170 pidfd = stress_pidfd_open(pid, 0);
171 if (pidfd < 0) {
172 /* Process not found, try again */
173 stress_pidfd_reap(pid, pidfd);
174 continue;
175 }
176
177 /* Exercise fstat'ing the pidfd */
178 ret = fstat(pidfd, &statbuf);
179 (void)ret;
180
181 /* Exercise illegal mmap'ing the pidfd */
182 ptr = mmap(NULL, args->page_size, PROT_READ,
183 MAP_PRIVATE, pidfd, 0);
184 if (ptr != MAP_FAILED)
185 (void)munmap(ptr, args->page_size);
186
187 /* Try to get fd 0 on child pid */
188 ret = shim_pidfd_getfd(pidfd, 0, 0);
189 if (ret >= 0)
190 (void)close(ret);
191
192 /* Exercise with invalid flags */
193 ret = shim_pidfd_getfd(pidfd, 0, ~0U);
194 if (ret >= 0)
195 (void)close(ret);
196
197 /* Exercise with bad_fd */
198 ret = shim_pidfd_getfd(pidfd, bad_fd, 0);
199 if (ret >= 0)
200 (void)close(ret);
201
202 ret = shim_pidfd_send_signal(pidfd, 0, NULL, 0);
203 if (ret != 0) {
204 if (errno == ENOSYS) {
205 if (args->instance == 0)
206 pr_inf_skip("%s: skipping stress test, system call is not implemented\n",
207 args->name);
208 stress_pidfd_reap(pid, pidfd);
209 return EXIT_NOT_IMPLEMENTED;
210 }
211 pr_err("%s: pidfd_send_signal failed: errno=%d (%s)\n",
212 args->name, errno, strerror(errno));
213 stress_pidfd_reap(pid, pidfd);
214 break;
215 }
216 ret = shim_pidfd_send_signal(pidfd, SIGSTOP, NULL, 0);
217 if (ret != 0) {
218 pr_err("%s: pidfd_send_signal (SIGSTOP), failed: errno=%d (%s)\n",
219 args->name, errno, strerror(errno));
220 }
221 ret = shim_pidfd_send_signal(pidfd, SIGCONT, NULL, 0);
222 if (ret != 0) {
223 pr_err("%s: pidfd_send_signal (SIGCONT), failed: errno=%d (%s)\n",
224 args->name, errno, strerror(errno));
225 }
226 stress_pidfd_reap(pid, pidfd);
227 }
228 inc_counter(args);
229 }
230 finish:
231 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
232
233 return EXIT_SUCCESS;
234 }
235
236 stressor_info_t stress_pidfd_info = {
237 .stressor = stress_pidfd,
238 .class = CLASS_INTERRUPT | CLASS_OS,
239 .supported = stress_pidfd_supported,
240 .help = help
241 };
242 #else
243
stress_pidfd_supported(const char * name)244 static int stress_pidfd_supported(const char *name)
245 {
246 pr_inf_skip("%s: stressor will be skipped, system call not supported at build time\n", name);
247 return -1;
248 }
249
250 stressor_info_t stress_pidfd_info = {
251 .stressor = stress_not_implemented,
252 .class = CLASS_INTERRUPT | CLASS_OS,
253 .supported = stress_pidfd_supported,
254 .help = help
255 };
256 #endif
257