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, "close N", "start N workers that exercise races on close" },
29 { NULL, "close-ops N", "stop after N bogo close operations" },
30 { NULL, NULL, NULL }
31 };
32
33 #if defined(HAVE_LIB_PTHREAD)
34
35 #define MAX_PTHREADS (3)
36 #define SHM_NAME_LEN (128)
37 #define FDS_START (1024)
38 #define FDS_TO_DUP (8)
39
40 static volatile int fd, dupfd;
41 static volatile uint64_t max_delay_us = 1;
42 static sigset_t set;
43
44 static const int domains[] = {
45 #if defined(AF_UNIX)
46 AF_UNIX,
47 #endif
48 #if defined(AF_LOCAL)
49 AF_LOCAL,
50 #endif
51 #if defined(AF_INET)
52 AF_INET,
53 #endif
54 #if defined(AF_INET6)
55 AF_INET6,
56 #endif
57 #if defined(AF_IPX)
58 AF_IPX,
59 #endif
60 #if defined(AF_NETLINK)
61 AF_NETLINK,
62 #endif
63 #if defined(AF_X25)
64 AF_X25,
65 #endif
66 #if defined(AF_AX25)
67 AF_AX25,
68 #endif
69 #if defined(AF_ATMPVC)
70 AF_ATMPVC,
71 #endif
72 /*
73 #if defined(AF_APPLETALK)
74 AF_APPLETALK,
75 #endif
76 */
77 #if defined(AF_PACKET)
78 AF_PACKET,
79 #endif
80 #if defined(AF_ALG)
81 AF_ALG,
82 #endif
83 0,
84 };
85
86 static const int types[] = {
87 #if defined(SOCK_STREAM)
88 SOCK_STREAM,
89 #endif
90 #if defined(SOCK_DGRAM)
91 SOCK_DGRAM,
92 #endif
93 #if defined(SOCK_SEQPACKET)
94 SOCK_SEQPACKET,
95 #endif
96 #if defined(SOCK_RAW)
97 SOCK_RAW,
98 #endif
99 #if defined(SOCK_RDM)
100 SOCK_RDM,
101 #endif
102 0,
103 };
104
105 /*
106 * stress_close_func()
107 * pthread that exits immediately
108 */
stress_close_func(void * arg)109 static void *stress_close_func(void *arg)
110 {
111 static void *nowt = NULL;
112 stress_pthread_args_t *pargs = (stress_pthread_args_t *)arg;
113 const stress_args_t *args = pargs->args;
114
115 /*
116 * Block all signals, let controlling thread
117 * handle these
118 */
119 #if !defined(__APPLE__) && \
120 !defined(__DragonFly__)
121 (void)sigprocmask(SIG_BLOCK, &set, NULL);
122 #endif
123
124 while (keep_stressing(args)) {
125 int fd_rnd = (int)stress_mwc32() + 64;
126 const uint64_t delay =
127 max_delay_us ? stress_mwc32() % max_delay_us : 0;
128 int fds[FDS_TO_DUP], i, ret;
129
130 for (i = 0; i < FDS_TO_DUP; i++) {
131 fds[i] = dup2(fileno(stderr), i + FDS_START);
132 }
133
134 shim_usleep_interruptible(delay);
135 if (fd != -1)
136 (void)close(fd);
137 if (dupfd != -1)
138 (void)close(dupfd);
139
140 #if defined(F_GETFL)
141 /*
142 * close random unused fd to force EBADF
143 */
144 if (fcntl((int)fd_rnd, F_GETFL) == -1)
145 (void)close(fd_rnd);
146 #endif
147 /*
148 * close a range of fds
149 */
150 ret = shim_close_range(FDS_START, FDS_START + FDS_TO_DUP, 0);
151 if ((ret < 1) && (errno == ENOSYS)) {
152 for (i = 0; i < FDS_TO_DUP; i++)
153 (void)close(fds[i]);
154 }
155 /*
156 * close an invalid range of fds
157 */
158 ret = shim_close_range(FDS_START + FDS_TO_DUP, FDS_START, 0);
159 (void)ret;
160
161 /*
162 * close with invalid fds and flags
163 */
164 ret = shim_close_range(FDS_START + FDS_TO_DUP, FDS_START, ~0U);
165 (void)ret;
166 }
167
168 return &nowt;
169 }
170
171 /*
172 * stress_close()
173 * stress by creating pthreads
174 */
stress_close(const stress_args_t * args)175 static int stress_close(const stress_args_t *args)
176 {
177 stress_pthread_args_t pargs;
178 pthread_t pthread[MAX_PTHREADS];
179 int rc = EXIT_NO_RESOURCE;
180 int ret, rets[MAX_PTHREADS];
181 const int bad_fd = stress_get_bad_fd();
182 size_t i;
183 const uid_t uid = getuid();
184 const gid_t gid = getgid();
185 const bool not_root = !stress_check_capability(SHIM_CAP_IS_ROOT);
186 double max_duration = 0.0;
187 #if defined(HAVE_FACCESSAT)
188 int file_fd = -1;
189 char filename[PATH_MAX];
190 #endif
191 #if defined(HAVE_LIB_RT)
192 char shm_name[SHM_NAME_LEN];
193 #endif
194
195 #if defined(HAVE_LIB_RT)
196 (void)snprintf(shm_name, SHM_NAME_LEN,
197 "stress-ng-%d-%" PRIx32, (int)getpid(), stress_mwc32());
198 #endif
199
200 (void)sigfillset(&set);
201
202 fd = -1;
203 dupfd = -1;
204
205 pargs.args = args;
206 pargs.data = NULL;
207
208 for (i = 0; i < MAX_PTHREADS; i++)
209 rets[i] = -1;
210
211 for (i = 0; i < MAX_PTHREADS; i++) {
212 rets[i] = pthread_create(&pthread[i], NULL, stress_close_func, (void *)&pargs);
213 if (rets[i]) {
214 pr_inf("%s: failed to create a pthread, error=%d (%s)\n",
215 args->name, rets[i], strerror(rets[i]));
216 goto tidy;
217 }
218 }
219
220 #if defined(HAVE_FACCESSAT)
221 {
222 ret = stress_temp_dir_mk_args(args);
223 if (ret < 0)
224 return exit_status(-ret);
225 (void)stress_temp_filename_args(args, filename, sizeof(filename), stress_mwc32());
226 file_fd = open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
227 if (file_fd < 0) {
228 pr_err("%s: cannot create %s\n", args->name, filename);
229 return exit_status(errno);
230 }
231 (void)unlink(filename);
232 }
233 #endif
234 stress_set_proc_state(args->name, STRESS_STATE_RUN);
235
236 do {
237 size_t domain, type;
238 int pipefds[2];
239 struct stat statbuf;
240 fd = -1;
241 double t1, t2, duration;
242
243 t1 = stress_time_now();
244
245 switch (stress_mwc8() % 14) {
246 case 0:
247 domain = stress_mwc8() % SIZEOF_ARRAY(domains); /* cppcheck-suppress moduloofone */
248 type = stress_mwc8() % SIZEOF_ARRAY(types); /* cppcheck-suppress moduloofone */
249 fd = socket(domains[domain], types[type], 0);
250 break;
251 case 1:
252 fd = open("/dev/zero", O_RDONLY);
253 break;
254 #if defined(O_TMPFILE)
255 case 2:
256 fd = open("/tmp", O_TMPFILE | O_RDWR,
257 S_IRUSR | S_IWUSR);
258 break;
259 #endif
260 #if defined(HAVE_SYS_EPOLL_H)
261 case 3:
262 fd = epoll_create(1);
263 break;
264 #endif
265 #if defined(HAVE_SYS_EVENTFD_H) && \
266 defined(HAVE_EVENTFD) && \
267 NEED_GLIBC(2,8,0)
268 case 4:
269 fd = eventfd(0, 0);
270 break;
271 #endif
272 #if defined(HAVE_SYS_FANOTIFY_H) && \
273 defined(HAVE_FANOTIFY)
274 case 5:
275 fd = fanotify_init(0, 0);
276 break;
277 #endif
278 #if defined(HAVE_INOTIFY) && \
279 defined(HAVE_SYS_INOTIFY_H)
280 case 6:
281 fd = inotify_init();
282 break;
283 #endif
284 case 7:
285 if (pipe(pipefds) == 0) {
286 fd = pipefds[0];
287 (void)close(pipefds[1]);
288 }
289 break;
290 #if defined(HAVE_SYS_SIGNALFD_H) && \
291 NEED_GLIBC(2,8,0) && \
292 defined(HAVE_SIGQUEUE) && \
293 defined(SIGRTMIN)
294 case 8:
295 {
296 sigset_t mask;
297
298 (void)sigemptyset(&mask);
299 (void)sigaddset(&mask, SIGRTMIN);
300 fd = signalfd(-1, &mask, 0);
301 }
302 break;
303 #endif
304 #if defined(HAVE_USERFAULTFD) && \
305 defined(HAVE_LINUX_USERFAULTFD_H)
306 case 9:
307 fd = shim_userfaultfd(0);
308 break;
309 #endif
310 #if defined(O_PATH)
311 case 10:
312 fd = open("/tmp", O_PATH | O_RDWR);
313 break;
314 #endif
315 #if defined(O_DIRECTORY) && \
316 defined(O_CLOEXEC)
317 case 11:
318 fd = open("/tmp/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
319 break;
320 #endif
321 #if defined(HAVE_LIB_RT)
322 case 12:
323 fd = shm_open(shm_name, O_CREAT | O_RDWR | O_TRUNC,
324 S_IRUSR | S_IWUSR);
325 (void)shm_unlink(shm_name);
326 break;
327 #endif
328 case 13:
329 fd = bad_fd;
330 break;
331 default:
332 break;
333 }
334 if (fd == -1)
335 fd = open("/dev/null", O_RDWR);
336
337 if (fd != -1) {
338 dupfd = dup(fd);
339 if (not_root) {
340 #if defined(HAVE_FCHOWNAT)
341 ret = fchownat(fd, "", uid, gid, 0);
342 (void)ret;
343
344 ret = fchownat(fd, "", uid, gid, ~0);
345 (void)ret;
346 #endif
347 ret = fchown(fd, uid, gid);
348 (void)ret;
349 }
350 #if defined(HAVE_FACCESSAT)
351 ret = faccessat(fd, "", F_OK, 0);
352 (void)ret;
353
354 /*
355 * Exercise bad dirfd resulting in Error EBADF
356 */
357 ret = faccessat(bad_fd, "", F_OK, 0);
358 (void)ret;
359
360 /*
361 * Exercise invalid flags syscall
362 */
363 ret = faccessat(fd, "", ~0, 0);
364 (void)ret;
365
366 /*
367 * Invalid faccessat syscall with pathname is relative and dirfd
368 * is a file descriptor referring to a file other than a directory
369 */
370 ret = faccessat(file_fd, "./", F_OK, 0);
371 if (ret >= 0) {
372 pr_fail("%s: faccessat opened file descriptor succeeded unexpectedly, "
373 "errno=%d (%s)\n", args->name, errno, strerror(errno));
374 (void)close(ret);
375 goto tidy;
376 }
377 #endif
378 ret = fstat(fd, &statbuf);
379 (void)ret;
380
381 (void)close(fd);
382 if (dupfd != -1)
383 (void)close(dupfd);
384 }
385 t2 = stress_time_now();
386 duration = t2 - t1;
387 if (duration > max_duration) {
388 max_duration = duration;
389 /* max delay is 75% of the duration in microseconds */
390 max_delay_us = (uint64_t)(duration * 750000);
391 }
392
393 max_duration *= 0.995;
394 if (max_duration < 1.0)
395 max_duration = 1.0;
396 inc_counter(args);
397 } while (keep_stressing(args));
398
399 rc = EXIT_SUCCESS;
400 tidy:
401 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
402
403 for (i = 0; i < MAX_PTHREADS; i++) {
404 if (rets[i] == -1)
405 continue;
406 ret = pthread_join(pthread[i], NULL);
407 if ((ret) && (ret != ESRCH)) {
408 pr_fail("%s: pthread_join failed (parent), errno=%d (%s)\n",
409 args->name, ret, strerror(ret));
410 }
411 }
412
413 #if defined(HAVE_FACCESSAT)
414 if (file_fd >= 0)
415 (void)close(file_fd);
416 (void)stress_temp_dir_rm_args(args);
417 #endif
418
419 return rc;
420 }
421
422 stressor_info_t stress_close_info = {
423 .stressor = stress_close,
424 .class = CLASS_SCHEDULER | CLASS_OS,
425 .help = help
426 };
427 #else
428 stressor_info_t stress_close_info = {
429 .stressor = stress_not_implemented,
430 .class = CLASS_SCHEDULER | CLASS_OS,
431 .help = help
432 };
433 #endif
434