Commit | Line | Data |
---|---|---|
9d16b343 MJ |
1 | /* |
2 | * Copyright (C) 2016 Julien Desfossez <jdesfossez@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0-only | |
5 | * | |
6 | */ | |
7 | ||
fbf606d7 MJ |
8 | /* |
9 | * This test voluntarily does buffer overflows and stack overruns, disable | |
10 | * source fortification. | |
11 | */ | |
12 | #ifdef _FORTIFY_SOURCE | |
13 | #undef _FORTIFY_SOURCE | |
14 | #endif | |
15 | ||
28ab034a JG |
16 | #include <common/compat/time.hpp> |
17 | #include <common/error.hpp> | |
18 | ||
c9e313bc SM |
19 | #include <fcntl.h> |
20 | #include <limits.h> | |
a0b1f42c | 21 | #include <poll.h> |
c9e313bc SM |
22 | #include <popt.h> |
23 | #include <pthread.h> | |
a0b1f42c | 24 | #include <signal.h> |
c9e313bc SM |
25 | #include <stddef.h> |
26 | #include <stdio.h> | |
a0b1f42c JD |
27 | #include <stdlib.h> |
28 | #include <string.h> | |
a0b1f42c | 29 | #include <sys/epoll.h> |
a0b1f42c | 30 | #include <sys/mman.h> |
c9e313bc SM |
31 | #include <sys/resource.h> |
32 | #include <sys/select.h> | |
33 | #include <sys/stat.h> | |
34 | #include <sys/syscall.h> | |
35 | #include <sys/time.h> | |
36 | #include <sys/types.h> | |
37 | #include <unistd.h> | |
38 | ||
a0b1f42c | 39 | #define BUF_SIZE 256 |
28ab034a JG |
40 | #define NB_FD 1 |
41 | #define MAX_FDS 2047 | |
42 | #define NR_ITER 1000 /* for stress-tests */ | |
a0b1f42c | 43 | |
28ab034a | 44 | #define MIN_NR_FDS 5 /* the minimum number of open FDs required for the test to run */ |
a0b1f42c JD |
45 | #define BIG_SELECT_FD 1022 |
46 | ||
47 | #define MSEC_PER_USEC 1000 | |
48 | #define MSEC_PER_NSEC (MSEC_PER_USEC * 1000) | |
49 | ||
50 | static int timeout; /* seconds, -1 to disable */ | |
44a411a6 | 51 | static volatile int stop_thread; |
a0b1f42c JD |
52 | static int wait_fd; |
53 | ||
d40c2620 JG |
54 | /* Used by logging utils. */ |
55 | int lttng_opt_quiet, lttng_opt_verbose, lttng_opt_mi; | |
56 | ||
57 | static void run_working_cases(FILE *validation_output_file); | |
58 | static void pselect_invalid_fd(FILE *validation_output_file); | |
59 | static void test_ppoll_big(FILE *validation_output_file); | |
60 | static void ppoll_fds_buffer_overflow(FILE *validation_output_file); | |
61 | static void pselect_invalid_pointer(FILE *validation_output_file); | |
62 | static void ppoll_fds_ulong_max(FILE *validation_output_file); | |
63 | static void epoll_pwait_invalid_pointer(FILE *validation_output_file); | |
64 | static void epoll_pwait_int_max(FILE *validation_output_file); | |
65 | static void ppoll_concurrent_write(FILE *validation_output_file); | |
66 | static void epoll_pwait_concurrent_munmap(FILE *validation_output_file); | |
67 | ||
e665dfbc | 68 | using test_case_cb = void (*)(FILE *); |
d40c2620 | 69 | |
f1494934 JG |
70 | namespace { |
71 | const struct test_case { | |
d40c2620 JG |
72 | test_case_cb run; |
73 | bool produces_validation_info; | |
74 | int timeout; | |
28ab034a | 75 | } test_cases[] = { |
d40c2620 JG |
76 | { .run = run_working_cases, .produces_validation_info = true, .timeout = -1 }, |
77 | { .run = run_working_cases, .produces_validation_info = true, .timeout = 1 }, | |
1c9a0b0e MJ |
78 | { .run = pselect_invalid_fd, .produces_validation_info = false, .timeout = 0 }, |
79 | { .run = test_ppoll_big, .produces_validation_info = false, .timeout = 0 }, | |
80 | { .run = ppoll_fds_buffer_overflow, .produces_validation_info = false, .timeout = 0 }, | |
81 | { .run = pselect_invalid_pointer, .produces_validation_info = false, .timeout = 0 }, | |
82 | { .run = ppoll_fds_ulong_max, .produces_validation_info = false, .timeout = 0 }, | |
83 | { .run = epoll_pwait_invalid_pointer, .produces_validation_info = true, .timeout = 0 }, | |
84 | { .run = epoll_pwait_int_max, .produces_validation_info = true, .timeout = 0 }, | |
85 | { .run = ppoll_concurrent_write, .produces_validation_info = false, .timeout = 0 }, | |
86 | { .run = epoll_pwait_concurrent_munmap, .produces_validation_info = true, .timeout = 0 }, | |
d40c2620 JG |
87 | }; |
88 | ||
a0b1f42c JD |
89 | struct ppoll_thread_data { |
90 | struct pollfd *ufds; | |
91 | int value; | |
92 | }; | |
f1494934 | 93 | } /* namespace */ |
a0b1f42c | 94 | |
cd9adb8b | 95 | static void test_select_big() |
a0b1f42c JD |
96 | { |
97 | fd_set rfds, wfds, exfds; | |
98 | struct timeval tv; | |
99 | int ret; | |
100 | int fd2; | |
101 | char buf[BUF_SIZE]; | |
102 | ||
103 | FD_ZERO(&rfds); | |
104 | FD_ZERO(&wfds); | |
105 | FD_ZERO(&exfds); | |
106 | ||
107 | fd2 = dup2(wait_fd, BIG_SELECT_FD); | |
108 | if (fd2 < 0) { | |
d40c2620 | 109 | PERROR("dup2"); |
a0b1f42c JD |
110 | goto end; |
111 | } | |
112 | FD_SET(fd2, &rfds); | |
113 | ||
114 | tv.tv_sec = 0; | |
115 | tv.tv_usec = timeout * MSEC_PER_USEC; | |
116 | ||
117 | if (timeout > 0) { | |
118 | ret = select(fd2 + 1, &rfds, &wfds, &exfds, &tv); | |
119 | } else { | |
cd9adb8b | 120 | ret = select(fd2 + 1, &rfds, &wfds, &exfds, nullptr); |
a0b1f42c JD |
121 | } |
122 | ||
123 | if (ret == -1) { | |
d40c2620 | 124 | PERROR("select()"); |
a0b1f42c | 125 | } else if (ret) { |
a0b1f42c JD |
126 | ret = read(wait_fd, buf, BUF_SIZE); |
127 | if (ret < 0) { | |
d40c2620 | 128 | PERROR("[select] read"); |
a0b1f42c | 129 | } |
a0b1f42c JD |
130 | } |
131 | ||
132 | ret = close(BIG_SELECT_FD); | |
133 | if (ret) { | |
d40c2620 | 134 | PERROR("close"); |
a0b1f42c JD |
135 | } |
136 | ||
137 | end: | |
138 | return; | |
139 | } | |
140 | ||
cd9adb8b | 141 | static void test_pselect() |
a0b1f42c JD |
142 | { |
143 | fd_set rfds; | |
144 | struct timespec tv; | |
145 | int ret; | |
146 | char buf[BUF_SIZE]; | |
147 | ||
148 | FD_ZERO(&rfds); | |
149 | FD_SET(wait_fd, &rfds); | |
150 | ||
151 | tv.tv_sec = 0; | |
152 | tv.tv_nsec = timeout * MSEC_PER_NSEC; | |
153 | ||
154 | if (timeout > 0) { | |
cd9adb8b | 155 | ret = pselect(1, &rfds, nullptr, nullptr, &tv, nullptr); |
a0b1f42c | 156 | } else { |
cd9adb8b | 157 | ret = pselect(1, &rfds, nullptr, nullptr, nullptr, nullptr); |
a0b1f42c JD |
158 | } |
159 | ||
160 | if (ret == -1) { | |
d40c2620 | 161 | PERROR("pselect()"); |
a0b1f42c | 162 | } else if (ret) { |
a0b1f42c JD |
163 | ret = read(wait_fd, buf, BUF_SIZE); |
164 | if (ret < 0) { | |
d40c2620 | 165 | PERROR("[pselect] read"); |
a0b1f42c | 166 | } |
a0b1f42c | 167 | } |
a0b1f42c JD |
168 | } |
169 | ||
cd9adb8b | 170 | static void test_select() |
a0b1f42c JD |
171 | { |
172 | fd_set rfds; | |
173 | struct timeval tv; | |
174 | int ret; | |
175 | char buf[BUF_SIZE]; | |
176 | ||
177 | FD_ZERO(&rfds); | |
178 | FD_SET(wait_fd, &rfds); | |
179 | ||
180 | tv.tv_sec = 0; | |
181 | tv.tv_usec = timeout * MSEC_PER_USEC; | |
182 | ||
183 | if (timeout > 0) { | |
cd9adb8b | 184 | ret = select(1, &rfds, nullptr, nullptr, &tv); |
a0b1f42c | 185 | } else { |
cd9adb8b | 186 | ret = select(1, &rfds, nullptr, nullptr, nullptr); |
a0b1f42c JD |
187 | } |
188 | ||
189 | if (ret == -1) { | |
d40c2620 | 190 | PERROR("select()"); |
a0b1f42c | 191 | } else if (ret) { |
a0b1f42c JD |
192 | ret = read(wait_fd, buf, BUF_SIZE); |
193 | if (ret < 0) { | |
d40c2620 | 194 | PERROR("[select] read"); |
a0b1f42c | 195 | } |
a0b1f42c | 196 | } |
a0b1f42c JD |
197 | } |
198 | ||
cd9adb8b | 199 | static void test_poll() |
a0b1f42c JD |
200 | { |
201 | struct pollfd ufds[NB_FD]; | |
202 | char buf[BUF_SIZE]; | |
203 | int ret; | |
204 | ||
205 | ufds[0].fd = wait_fd; | |
28ab034a | 206 | ufds[0].events = POLLIN | POLLPRI; |
a0b1f42c JD |
207 | |
208 | ret = poll(ufds, 1, timeout); | |
209 | ||
210 | if (ret < 0) { | |
d40c2620 | 211 | PERROR("poll"); |
a0b1f42c | 212 | } else if (ret > 0) { |
a0b1f42c JD |
213 | ret = read(wait_fd, buf, BUF_SIZE); |
214 | if (ret < 0) { | |
d40c2620 | 215 | PERROR("[poll] read"); |
a0b1f42c | 216 | } |
a0b1f42c JD |
217 | } |
218 | } | |
219 | ||
cd9adb8b | 220 | static void test_ppoll() |
a0b1f42c JD |
221 | { |
222 | struct pollfd ufds[NB_FD]; | |
223 | char buf[BUF_SIZE]; | |
224 | int ret; | |
225 | struct timespec ts; | |
226 | ||
227 | ufds[0].fd = wait_fd; | |
28ab034a | 228 | ufds[0].events = POLLIN | POLLPRI; |
a0b1f42c JD |
229 | |
230 | if (timeout > 0) { | |
231 | ts.tv_sec = 0; | |
232 | ts.tv_nsec = timeout * MSEC_PER_NSEC; | |
cd9adb8b | 233 | ret = ppoll(ufds, 1, &ts, nullptr); |
a0b1f42c | 234 | } else { |
cd9adb8b | 235 | ret = ppoll(ufds, 1, nullptr, nullptr); |
a0b1f42c JD |
236 | } |
237 | ||
a0b1f42c | 238 | if (ret < 0) { |
d40c2620 | 239 | PERROR("ppoll"); |
a0b1f42c | 240 | } else if (ret > 0) { |
a0b1f42c JD |
241 | ret = read(wait_fd, buf, BUF_SIZE); |
242 | if (ret < 0) { | |
d40c2620 | 243 | PERROR("[ppoll] read"); |
a0b1f42c | 244 | } |
a0b1f42c JD |
245 | } |
246 | } | |
247 | ||
28ab034a | 248 | static void test_ppoll_big(FILE *validation_output_file __attribute__((unused))) |
a0b1f42c JD |
249 | { |
250 | struct pollfd ufds[MAX_FDS]; | |
251 | char buf[BUF_SIZE]; | |
252 | int ret, i, fds[MAX_FDS]; | |
253 | ||
254 | for (i = 0; i < MAX_FDS; i++) { | |
255 | fds[i] = dup(wait_fd); | |
256 | if (fds[i] < 0) { | |
d40c2620 | 257 | PERROR("dup"); |
a0b1f42c JD |
258 | } |
259 | ufds[i].fd = fds[i]; | |
28ab034a | 260 | ufds[i].events = POLLIN | POLLPRI; |
a0b1f42c JD |
261 | } |
262 | ||
cd9adb8b | 263 | ret = ppoll(ufds, MAX_FDS, nullptr, nullptr); |
a0b1f42c JD |
264 | |
265 | if (ret < 0) { | |
d40c2620 | 266 | PERROR("ppoll"); |
a0b1f42c | 267 | } else if (ret > 0) { |
a0b1f42c JD |
268 | ret = read(wait_fd, buf, BUF_SIZE); |
269 | if (ret < 0) { | |
d40c2620 | 270 | PERROR("[ppoll] read"); |
a0b1f42c | 271 | } |
a0b1f42c JD |
272 | } |
273 | ||
274 | for (i = 0; i < MAX_FDS; i++) { | |
275 | ret = close(fds[i]); | |
276 | if (ret != 0) { | |
d40c2620 | 277 | PERROR("close"); |
a0b1f42c JD |
278 | } |
279 | } | |
280 | ||
281 | return; | |
282 | } | |
283 | ||
28ab034a | 284 | static void test_epoll(FILE *validation_output_file) |
a0b1f42c JD |
285 | { |
286 | int ret, epollfd; | |
287 | char buf[BUF_SIZE]; | |
288 | struct epoll_event epoll_event; | |
289 | ||
290 | epollfd = epoll_create(NB_FD); | |
291 | if (epollfd < 0) { | |
d40c2620 | 292 | PERROR("[epoll] create"); |
a0b1f42c JD |
293 | goto end; |
294 | } | |
295 | ||
28ab034a | 296 | ret = fprintf(validation_output_file, ", \"epoll_wait_fd\": %i", epollfd); |
d40c2620 JG |
297 | if (ret < 0) { |
298 | PERROR("[epoll] Failed to write test validation output"); | |
299 | goto error; | |
300 | } | |
301 | ||
a0b1f42c JD |
302 | epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET; |
303 | epoll_event.data.fd = wait_fd; | |
304 | ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event); | |
305 | if (ret < 0) { | |
d40c2620 | 306 | PERROR("[epoll] add"); |
5305e823 | 307 | goto error; |
a0b1f42c JD |
308 | } |
309 | ||
310 | if (timeout > 0) { | |
311 | ret = epoll_wait(epollfd, &epoll_event, 1, timeout); | |
312 | } else { | |
313 | ret = epoll_wait(epollfd, &epoll_event, 1, -1); | |
314 | } | |
315 | ||
316 | if (ret == 1) { | |
a0b1f42c JD |
317 | ret = read(wait_fd, buf, BUF_SIZE); |
318 | if (ret < 0) { | |
d40c2620 | 319 | PERROR("[epoll] read"); |
a0b1f42c | 320 | } |
d40c2620 JG |
321 | } else if (ret != 0) { |
322 | PERROR("epoll_wait"); | |
a0b1f42c JD |
323 | } |
324 | ||
5305e823 | 325 | error: |
6d52f14d FD |
326 | ret = close(epollfd); |
327 | if (ret) { | |
d40c2620 | 328 | PERROR("close"); |
6d52f14d | 329 | } |
a0b1f42c JD |
330 | end: |
331 | return; | |
332 | } | |
333 | ||
28ab034a | 334 | static void test_epoll_pwait(FILE *validation_output_file) |
a0b1f42c JD |
335 | { |
336 | int ret, epollfd; | |
337 | char buf[BUF_SIZE]; | |
338 | struct epoll_event epoll_event; | |
339 | ||
340 | epollfd = epoll_create(NB_FD); | |
341 | if (epollfd < 0) { | |
d40c2620 | 342 | PERROR("[epoll_pwait] create"); |
a0b1f42c JD |
343 | goto end; |
344 | } | |
345 | ||
28ab034a | 346 | ret = fprintf(validation_output_file, ", \"epoll_pwait_fd\": %i", epollfd); |
d40c2620 JG |
347 | if (ret < 0) { |
348 | PERROR("[epoll_pwait] Failed to write test validation output"); | |
349 | goto error; | |
350 | } | |
351 | ||
a0b1f42c JD |
352 | epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET; |
353 | epoll_event.data.fd = wait_fd; | |
354 | ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event); | |
355 | if (ret < 0) { | |
d40c2620 | 356 | PERROR("[epoll_pwait] add"); |
5305e823 | 357 | goto error; |
a0b1f42c JD |
358 | } |
359 | ||
360 | if (timeout > 0) { | |
cd9adb8b | 361 | ret = epoll_pwait(epollfd, &epoll_event, 1, timeout, nullptr); |
a0b1f42c | 362 | } else { |
cd9adb8b | 363 | ret = epoll_pwait(epollfd, &epoll_event, 1, -1, nullptr); |
a0b1f42c JD |
364 | } |
365 | ||
366 | if (ret == 1) { | |
a0b1f42c JD |
367 | ret = read(wait_fd, buf, BUF_SIZE); |
368 | if (ret < 0) { | |
d40c2620 | 369 | PERROR("[epoll_pwait] read"); |
a0b1f42c | 370 | } |
d40c2620 JG |
371 | } else if (ret != 0) { |
372 | PERROR("epoll_pwait"); | |
a0b1f42c JD |
373 | } |
374 | ||
5305e823 | 375 | error: |
6d52f14d FD |
376 | ret = close(epollfd); |
377 | if (ret) { | |
d40c2620 | 378 | PERROR("close"); |
6d52f14d | 379 | } |
a0b1f42c JD |
380 | end: |
381 | return; | |
382 | } | |
383 | ||
28ab034a | 384 | static void run_working_cases(FILE *validation_output_file) |
a0b1f42c JD |
385 | { |
386 | int ret; | |
387 | int pipe_fds[2]; | |
388 | ||
389 | if (timeout > 0) { | |
390 | /* | |
391 | * We need an input pipe for some cases and stdin might | |
392 | * have random data, so we create a dummy pipe for this | |
393 | * test to make sure we are running under clean conditions. | |
394 | */ | |
395 | ret = pipe(pipe_fds); | |
396 | if (ret != 0) { | |
d40c2620 | 397 | PERROR("pipe"); |
a0b1f42c JD |
398 | goto end; |
399 | } | |
400 | wait_fd = pipe_fds[0]; | |
401 | } | |
402 | test_select(); | |
403 | test_pselect(); | |
404 | test_select_big(); | |
405 | test_poll(); | |
406 | test_ppoll(); | |
d40c2620 JG |
407 | |
408 | ret = fprintf(validation_output_file, "{ \"pid\": %i", getpid()); | |
409 | if (ret < 0) { | |
410 | PERROR("Failed to write pid to test validation file"); | |
411 | goto end; | |
412 | } | |
413 | ||
414 | test_epoll(validation_output_file); | |
415 | test_epoll_pwait(validation_output_file); | |
a0b1f42c JD |
416 | |
417 | if (timeout > 0) { | |
418 | ret = close(pipe_fds[0]); | |
419 | if (ret) { | |
d40c2620 | 420 | PERROR("close"); |
a0b1f42c JD |
421 | } |
422 | ret = close(pipe_fds[1]); | |
423 | if (ret) { | |
d40c2620 | 424 | PERROR("close"); |
a0b1f42c JD |
425 | } |
426 | } | |
427 | ||
d40c2620 JG |
428 | ret = fputs(" }", validation_output_file); |
429 | if (ret < 0) { | |
430 | PERROR("Failed to close JSON dictionary in test validation file"); | |
431 | goto end; | |
432 | } | |
433 | ||
a0b1f42c JD |
434 | end: |
435 | return; | |
436 | } | |
437 | ||
438 | /* | |
439 | * Ask for 100 FDs in a buffer for allocated for only 1 FD, should | |
440 | * segfault (eventually with a "*** stack smashing detected ***" message). | |
441 | * The event should contain an array of 100 FDs filled with garbage. | |
442 | */ | |
28ab034a | 443 | static void ppoll_fds_buffer_overflow(FILE *validation_output_file __attribute__((unused))) |
a0b1f42c JD |
444 | { |
445 | struct pollfd ufds[NB_FD]; | |
446 | char buf[BUF_SIZE]; | |
447 | int ret; | |
448 | ||
449 | ufds[0].fd = wait_fd; | |
28ab034a | 450 | ufds[0].events = POLLIN | POLLPRI; |
a0b1f42c JD |
451 | |
452 | ret = syscall(SYS_ppoll, ufds, 100, NULL, NULL); | |
453 | ||
454 | if (ret < 0) { | |
d40c2620 | 455 | PERROR("ppoll"); |
a0b1f42c | 456 | } else if (ret > 0) { |
a0b1f42c JD |
457 | ret = read(wait_fd, buf, BUF_SIZE); |
458 | if (ret < 0) { | |
d40c2620 | 459 | PERROR("[ppoll] read"); |
a0b1f42c | 460 | } |
a0b1f42c | 461 | } |
a0b1f42c JD |
462 | } |
463 | ||
464 | /* | |
465 | * Ask for ULONG_MAX FDs in a buffer for allocated for only 1 FD, should | |
466 | * cleanly fail with a "Invalid argument". | |
467 | * The event should contain an empty array of FDs and overflow = 1. | |
468 | */ | |
28ab034a | 469 | static void ppoll_fds_ulong_max(FILE *validation_output_file __attribute__((unused))) |
a0b1f42c JD |
470 | { |
471 | struct pollfd ufds[NB_FD]; | |
472 | char buf[BUF_SIZE]; | |
473 | int ret; | |
474 | ||
475 | ufds[0].fd = wait_fd; | |
28ab034a | 476 | ufds[0].events = POLLIN | POLLPRI; |
a0b1f42c JD |
477 | |
478 | ret = syscall(SYS_ppoll, ufds, ULONG_MAX, NULL, NULL); | |
a0b1f42c | 479 | if (ret < 0) { |
d40c2620 | 480 | /* Expected error. */ |
a0b1f42c | 481 | } else if (ret > 0) { |
a0b1f42c JD |
482 | ret = read(wait_fd, buf, BUF_SIZE); |
483 | if (ret < 0) { | |
d40c2620 | 484 | PERROR("[ppoll] read"); |
a0b1f42c | 485 | } |
a0b1f42c | 486 | } |
a0b1f42c JD |
487 | } |
488 | ||
489 | /* | |
8b3b99e2 FD |
490 | * Pass an invalid file descriptor to pselect6(). The syscall should return |
491 | * -EBADF. The recorded event should contain a "ret = -EBADF (-9)". | |
a0b1f42c | 492 | */ |
28ab034a | 493 | static void pselect_invalid_fd(FILE *validation_output_file __attribute__((unused))) |
a0b1f42c | 494 | { |
8b3b99e2 | 495 | fd_set rfds; |
a0b1f42c | 496 | int ret; |
8b3b99e2 | 497 | int fd; |
a0b1f42c JD |
498 | char buf[BUF_SIZE]; |
499 | ||
500 | /* | |
8b3b99e2 | 501 | * Open a file, close it and use the closed FD in the pselect6 call. |
a0b1f42c | 502 | */ |
8b3b99e2 FD |
503 | fd = open("/dev/null", O_RDONLY); |
504 | if (fd == -1) { | |
d40c2620 | 505 | PERROR("open"); |
8b3b99e2 FD |
506 | goto error; |
507 | } | |
508 | ||
509 | ret = close(fd); | |
510 | if (ret == -1) { | |
d40c2620 | 511 | PERROR("close"); |
8b3b99e2 | 512 | goto error; |
a0b1f42c | 513 | } |
e593ee02 | 514 | |
8b3b99e2 FD |
515 | FD_ZERO(&rfds); |
516 | FD_SET(fd, &rfds); | |
a0b1f42c | 517 | |
8b3b99e2 | 518 | ret = syscall(SYS_pselect6, fd + 1, &rfds, NULL, NULL, NULL, NULL); |
a0b1f42c | 519 | if (ret == -1) { |
d40c2620 | 520 | /* Expected error. */ |
a0b1f42c | 521 | } else if (ret) { |
a0b1f42c JD |
522 | ret = read(wait_fd, buf, BUF_SIZE); |
523 | if (ret < 0) { | |
d40c2620 | 524 | PERROR("[pselect] read"); |
a0b1f42c | 525 | } |
a0b1f42c | 526 | } |
8b3b99e2 FD |
527 | error: |
528 | return; | |
a0b1f42c JD |
529 | } |
530 | ||
531 | /* | |
532 | * Invalid pointer as writefds, should output a ppoll event | |
533 | * with 0 FDs. | |
534 | */ | |
28ab034a | 535 | static void pselect_invalid_pointer(FILE *validation_output_file __attribute__((unused))) |
a0b1f42c JD |
536 | { |
537 | fd_set rfds; | |
538 | int ret; | |
539 | char buf[BUF_SIZE]; | |
540 | void *invalid = (void *) 0x42; | |
541 | ||
542 | FD_ZERO(&rfds); | |
543 | FD_SET(wait_fd, &rfds); | |
544 | ||
28ab034a | 545 | ret = syscall(SYS_pselect6, 1, &rfds, (fd_set *) invalid, NULL, NULL, NULL); |
a0b1f42c | 546 | if (ret == -1) { |
d40c2620 | 547 | /* Expected error. */ |
a0b1f42c | 548 | } else if (ret) { |
a0b1f42c JD |
549 | ret = read(wait_fd, buf, BUF_SIZE); |
550 | if (ret < 0) { | |
d40c2620 | 551 | PERROR("[pselect] read"); |
a0b1f42c | 552 | } |
a0b1f42c | 553 | } |
a0b1f42c JD |
554 | } |
555 | ||
556 | /* | |
557 | * Pass an invalid pointer to epoll_pwait, should fail with | |
558 | * "Bad address", the event returns 0 FDs. | |
559 | */ | |
28ab034a | 560 | static void epoll_pwait_invalid_pointer(FILE *validation_output_file) |
a0b1f42c JD |
561 | { |
562 | int ret, epollfd; | |
563 | char buf[BUF_SIZE]; | |
564 | struct epoll_event epoll_event; | |
565 | void *invalid = (void *) 0x42; | |
566 | ||
567 | epollfd = epoll_create(NB_FD); | |
568 | if (epollfd < 0) { | |
d40c2620 | 569 | PERROR("[epoll_pwait] create"); |
a0b1f42c JD |
570 | goto end; |
571 | } | |
572 | ||
28ab034a JG |
573 | ret = fprintf( |
574 | validation_output_file, "{ \"epollfd\": %i, \"pid\": %i }", epollfd, getpid()); | |
d40c2620 JG |
575 | if (ret < 0) { |
576 | PERROR("[epoll_pwait] Failed to write test validation output"); | |
577 | goto error; | |
578 | } | |
579 | ||
a0b1f42c JD |
580 | epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET; |
581 | epoll_event.data.fd = wait_fd; | |
582 | ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event); | |
583 | if (ret < 0) { | |
d40c2620 | 584 | PERROR("[epoll_pwait] add"); |
5305e823 | 585 | goto error; |
a0b1f42c JD |
586 | } |
587 | ||
28ab034a | 588 | ret = syscall(SYS_epoll_pwait, epollfd, (struct epoll_event *) invalid, 1, -1, NULL); |
a0b1f42c JD |
589 | |
590 | if (ret == 1) { | |
a0b1f42c JD |
591 | ret = read(wait_fd, buf, BUF_SIZE); |
592 | if (ret < 0) { | |
d40c2620 | 593 | PERROR("[epoll_pwait] read"); |
a0b1f42c | 594 | } |
d40c2620 JG |
595 | } else if (ret != 0) { |
596 | /* Expected error. */ | |
a0b1f42c JD |
597 | } |
598 | ||
5305e823 | 599 | error: |
6d52f14d FD |
600 | ret = close(epollfd); |
601 | if (ret) { | |
d40c2620 | 602 | PERROR("close"); |
6d52f14d | 603 | } |
a0b1f42c JD |
604 | end: |
605 | return; | |
606 | } | |
607 | ||
608 | /* | |
609 | * Set maxevents to INT_MAX, should output "Invalid argument" | |
610 | * The event should return an empty array. | |
611 | */ | |
28ab034a | 612 | static void epoll_pwait_int_max(FILE *validation_output_file) |
a0b1f42c JD |
613 | { |
614 | int ret, epollfd; | |
615 | char buf[BUF_SIZE]; | |
616 | struct epoll_event epoll_event; | |
617 | ||
618 | epollfd = epoll_create(NB_FD); | |
619 | if (epollfd < 0) { | |
d40c2620 | 620 | PERROR("[epoll_pwait] create"); |
a0b1f42c JD |
621 | goto end; |
622 | } | |
623 | ||
28ab034a JG |
624 | ret = fprintf( |
625 | validation_output_file, "{ \"epollfd\": %i, \"pid\": %i }", epollfd, getpid()); | |
d40c2620 JG |
626 | if (ret < 0) { |
627 | PERROR("[epoll_pwait] Failed to write test validation output"); | |
628 | goto error; | |
629 | } | |
630 | ||
a0b1f42c JD |
631 | epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET; |
632 | epoll_event.data.fd = wait_fd; | |
633 | ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event); | |
634 | if (ret < 0) { | |
d40c2620 | 635 | PERROR("[epoll_pwait] add"); |
5305e823 | 636 | goto error; |
a0b1f42c JD |
637 | } |
638 | ||
28ab034a | 639 | ret = syscall(SYS_epoll_pwait, epollfd, &epoll_event, INT_MAX, -1, NULL); |
a0b1f42c JD |
640 | |
641 | if (ret == 1) { | |
a0b1f42c JD |
642 | ret = read(wait_fd, buf, BUF_SIZE); |
643 | if (ret < 0) { | |
d40c2620 | 644 | PERROR("[epoll_pwait] read"); |
a0b1f42c | 645 | } |
d40c2620 JG |
646 | } else if (ret != 0) { |
647 | /* Expected error. */ | |
a0b1f42c JD |
648 | } |
649 | ||
5305e823 | 650 | error: |
6d52f14d FD |
651 | ret = close(epollfd); |
652 | if (ret) { | |
d40c2620 | 653 | PERROR("close"); |
6d52f14d | 654 | } |
a0b1f42c JD |
655 | end: |
656 | return; | |
657 | } | |
658 | ||
28ab034a | 659 | static void *ppoll_writer(void *arg) |
a0b1f42c JD |
660 | { |
661 | struct ppoll_thread_data *data = (struct ppoll_thread_data *) arg; | |
662 | ||
663 | while (!stop_thread) { | |
28ab034a | 664 | memset(data->ufds, data->value, MAX_FDS * sizeof(struct pollfd)); |
a0b1f42c JD |
665 | usleep(100); |
666 | } | |
667 | ||
cd9adb8b | 668 | return nullptr; |
a0b1f42c JD |
669 | } |
670 | ||
28ab034a | 671 | static void do_ppoll(int *fds, struct pollfd *ufds) |
a0b1f42c JD |
672 | { |
673 | int i, ret; | |
674 | struct timespec ts; | |
675 | char buf[BUF_SIZE]; | |
676 | ||
677 | ts.tv_sec = 0; | |
678 | ts.tv_nsec = 1 * MSEC_PER_NSEC; | |
679 | ||
680 | for (i = 0; i < MAX_FDS; i++) { | |
681 | ufds[i].fd = fds[i]; | |
28ab034a | 682 | ufds[i].events = POLLIN | POLLPRI; |
a0b1f42c JD |
683 | } |
684 | ||
cd9adb8b | 685 | ret = ppoll(ufds, MAX_FDS, &ts, nullptr); |
a0b1f42c JD |
686 | |
687 | if (ret < 0) { | |
d40c2620 | 688 | PERROR("ppoll"); |
a0b1f42c | 689 | } else if (ret > 0) { |
a0b1f42c JD |
690 | ret = read(wait_fd, buf, BUF_SIZE); |
691 | if (ret < 0) { | |
d40c2620 | 692 | PERROR("[ppoll] read"); |
a0b1f42c | 693 | } |
a0b1f42c JD |
694 | } |
695 | } | |
696 | ||
28ab034a | 697 | static void stress_ppoll(int *fds, int value) |
a0b1f42c JD |
698 | { |
699 | pthread_t writer; | |
700 | int iter, ret; | |
701 | struct ppoll_thread_data thread_data; | |
702 | struct pollfd ufds[MAX_FDS]; | |
703 | ||
704 | thread_data.ufds = ufds; | |
705 | thread_data.value = value; | |
706 | ||
707 | stop_thread = 0; | |
cd9adb8b | 708 | ret = pthread_create(&writer, nullptr, &ppoll_writer, (void *) &thread_data); |
a0b1f42c JD |
709 | if (ret != 0) { |
710 | fprintf(stderr, "[error] pthread_create\n"); | |
711 | goto end; | |
712 | } | |
713 | for (iter = 0; iter < NR_ITER; iter++) { | |
714 | do_ppoll(fds, ufds); | |
715 | } | |
716 | stop_thread = 1; | |
cd9adb8b | 717 | ret = pthread_join(writer, nullptr); |
9d558571 JG |
718 | if (ret) { |
719 | fprintf(stderr, "[error] pthread_join\n"); | |
720 | goto end; | |
721 | } | |
a0b1f42c JD |
722 | end: |
723 | return; | |
724 | } | |
725 | ||
726 | /* | |
727 | * 3 rounds of NR_ITER iterations with concurrent updates of the pollfd | |
728 | * structure: | |
729 | * - memset to 0 | |
730 | * - memset to 1 | |
731 | * - memset to INT_MAX | |
732 | * Waits for input, but also set a timeout in case the input FD is overwritten | |
733 | * before entering in the syscall. We use MAX_FDS FDs (dup of stdin), so the | |
734 | * resulting trace is big (20MB). | |
735 | * | |
736 | * ppoll should work as expected and the trace should be readable at the end. | |
737 | */ | |
28ab034a | 738 | static void ppoll_concurrent_write(FILE *validation_output_file __attribute__((unused))) |
a0b1f42c JD |
739 | { |
740 | int i, ret, fds[MAX_FDS]; | |
741 | ||
742 | for (i = 0; i < MAX_FDS; i++) { | |
743 | fds[i] = dup(wait_fd); | |
744 | if (fds[i] < 0) { | |
d40c2620 | 745 | PERROR("dup"); |
a0b1f42c JD |
746 | } |
747 | } | |
748 | ||
749 | stress_ppoll(fds, 0); | |
750 | stress_ppoll(fds, 1); | |
751 | stress_ppoll(fds, INT_MAX); | |
752 | ||
753 | for (i = 0; i < MAX_FDS; i++) { | |
754 | ret = close(fds[i]); | |
755 | if (ret != 0) { | |
d40c2620 | 756 | PERROR("close"); |
a0b1f42c JD |
757 | } |
758 | } | |
759 | ||
760 | return; | |
761 | } | |
762 | ||
28ab034a | 763 | static void *epoll_pwait_writer(void *addr) |
a0b1f42c | 764 | { |
cd9adb8b | 765 | srand(time(nullptr)); |
a0b1f42c JD |
766 | |
767 | while (!stop_thread) { | |
768 | usleep(rand() % 30); | |
769 | munmap(addr, MAX_FDS * sizeof(struct epoll_event)); | |
770 | } | |
771 | ||
cd9adb8b | 772 | return nullptr; |
a0b1f42c JD |
773 | } |
774 | ||
775 | /* | |
776 | * epoll_pwait on MAX_FDS fds while a concurrent thread munmaps the | |
777 | * buffer allocated for the returned data. This should randomly segfault. | |
778 | * The trace should be readable and no kernel OOPS should occur. | |
779 | */ | |
28ab034a | 780 | static void epoll_pwait_concurrent_munmap(FILE *validation_output_file) |
a0b1f42c JD |
781 | { |
782 | int ret, epollfd, i, fds[MAX_FDS]; | |
783 | char buf[BUF_SIZE]; | |
784 | struct epoll_event *epoll_event; | |
a0b1f42c JD |
785 | pthread_t writer; |
786 | ||
7f6288c8 JG |
787 | for (i = 0; i < MAX_FDS; i++) { |
788 | fds[i] = -1; | |
789 | } | |
a0b1f42c JD |
790 | epollfd = epoll_create(MAX_FDS); |
791 | if (epollfd < 0) { | |
d40c2620 | 792 | PERROR("[epoll_pwait] create"); |
a0b1f42c JD |
793 | goto end; |
794 | } | |
795 | ||
28ab034a JG |
796 | ret = fprintf( |
797 | validation_output_file, "{ \"epollfd\": %i, \"pid\": %i }", epollfd, getpid()); | |
d40c2620 JG |
798 | if (ret < 0) { |
799 | PERROR("[epoll_pwait] Failed to write test validation output"); | |
800 | goto error; | |
801 | } | |
802 | ||
cd9adb8b | 803 | epoll_event = (struct epoll_event *) mmap(nullptr, |
28ab034a JG |
804 | MAX_FDS * sizeof(struct epoll_event), |
805 | PROT_READ | PROT_WRITE, | |
806 | MAP_PRIVATE | MAP_ANONYMOUS, | |
807 | -1, | |
808 | 0); | |
a0b1f42c | 809 | if (epoll_event == MAP_FAILED) { |
d40c2620 | 810 | PERROR("mmap"); |
5305e823 | 811 | goto error; |
a0b1f42c JD |
812 | } |
813 | ||
814 | for (i = 0; i < MAX_FDS; i++) { | |
815 | fds[i] = dup(wait_fd); | |
816 | if (fds[i] < 0) { | |
d40c2620 | 817 | PERROR("dup"); |
a0b1f42c JD |
818 | } |
819 | epoll_event[i].events = EPOLLIN | EPOLLPRI | EPOLLET; | |
820 | epoll_event[i].data.fd = fds[i]; | |
821 | ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], epoll_event); | |
822 | if (ret < 0) { | |
d40c2620 | 823 | PERROR("[epoll_pwait] add"); |
5305e823 | 824 | goto error_unmap; |
a0b1f42c JD |
825 | } |
826 | } | |
827 | stop_thread = 0; | |
cd9adb8b | 828 | ret = pthread_create(&writer, nullptr, &epoll_pwait_writer, (void *) epoll_event); |
d542c0ae JG |
829 | if (ret != 0) { |
830 | fprintf(stderr, "[error] pthread_create\n"); | |
5305e823 | 831 | goto error_unmap; |
d542c0ae | 832 | } |
a0b1f42c | 833 | |
cd9adb8b | 834 | ret = epoll_pwait(epollfd, epoll_event, 1, 1, nullptr); |
a0b1f42c JD |
835 | |
836 | if (ret == 1) { | |
a0b1f42c JD |
837 | ret = read(wait_fd, buf, BUF_SIZE); |
838 | if (ret < 0) { | |
d40c2620 | 839 | PERROR("[epoll_pwait] read"); |
a0b1f42c | 840 | } |
d40c2620 JG |
841 | } else if (ret != 0) { |
842 | /* Expected error. */ | |
a0b1f42c JD |
843 | } |
844 | ||
845 | stop_thread = 1; | |
cd9adb8b | 846 | ret = pthread_join(writer, nullptr); |
9d558571 JG |
847 | if (ret) { |
848 | fprintf(stderr, "[error] pthread_join\n"); | |
5305e823 | 849 | goto error_unmap; |
9d558571 | 850 | } |
5305e823 | 851 | error_unmap: |
a0b1f42c JD |
852 | for (i = 0; i < MAX_FDS; i++) { |
853 | ret = close(fds[i]); | |
854 | if (ret != 0) { | |
d40c2620 | 855 | PERROR("close"); |
a0b1f42c JD |
856 | } |
857 | } | |
858 | ||
36bc42d9 | 859 | ret = munmap(epoll_event, MAX_FDS * sizeof(struct epoll_event)); |
a0b1f42c | 860 | if (ret != 0) { |
d40c2620 | 861 | PERROR("munmap"); |
a0b1f42c JD |
862 | } |
863 | ||
5305e823 | 864 | error: |
6d52f14d FD |
865 | ret = close(epollfd); |
866 | if (ret) { | |
d40c2620 | 867 | PERROR("close"); |
6d52f14d | 868 | } |
a0b1f42c JD |
869 | end: |
870 | return; | |
871 | } | |
872 | ||
cd9adb8b | 873 | static void print_list() |
a0b1f42c JD |
874 | { |
875 | fprintf(stderr, "Test list (-t X):\n"); | |
28ab034a JG |
876 | fprintf(stderr, |
877 | "\t1: Working cases for select, pselect6, poll, ppoll " | |
878 | "and epoll, waiting for input\n"); | |
879 | fprintf(stderr, | |
880 | "\t2: Timeout cases (1ms) for select, pselect6, poll, " | |
881 | "ppoll and epoll\n"); | |
8b3b99e2 | 882 | fprintf(stderr, "\t3: pselect with an invalid fd\n"); |
a0b1f42c | 883 | fprintf(stderr, "\t4: ppoll with %d FDs\n", MAX_FDS); |
28ab034a JG |
884 | fprintf(stderr, |
885 | "\t5: ppoll buffer overflow, should segfault, waits " | |
886 | "for input\n"); | |
887 | fprintf(stderr, | |
888 | "\t6: pselect with an invalid pointer, waits for " | |
889 | "input\n"); | |
a0b1f42c | 890 | fprintf(stderr, "\t7: ppoll with ulong_max fds, waits for input\n"); |
28ab034a JG |
891 | fprintf(stderr, |
892 | "\t8: epoll_pwait with an invalid pointer, waits for " | |
893 | "input\n"); | |
894 | fprintf(stderr, | |
895 | "\t9: epoll_pwait with maxevents set to INT_MAX, " | |
896 | "waits for input\n"); | |
897 | fprintf(stderr, | |
898 | "\t10: ppoll with concurrent updates of the structure " | |
899 | "from user-space, stress test (3000 iterations), " | |
900 | "waits for input + timeout 1ms\n"); | |
901 | fprintf(stderr, | |
902 | "\t11: epoll_pwait with concurrent munmap of the buffer " | |
903 | "from user-space, should randomly segfault, run " | |
904 | "multiple times, waits for input + timeout 1ms\n"); | |
a0b1f42c JD |
905 | } |
906 | ||
907 | int main(int argc, const char **argv) | |
908 | { | |
909 | int c, ret, test = -1; | |
910 | poptContext optCon; | |
911 | struct rlimit open_lim; | |
cd9adb8b JG |
912 | FILE *test_validation_output_file = nullptr; |
913 | const char *test_validation_output_file_path = nullptr; | |
a0b1f42c | 914 | struct poptOption optionsTable[] = { |
cd9adb8b JG |
915 | { "test", 't', POPT_ARG_INT, &test, 0, "Test to run", nullptr }, |
916 | { "list", 'l', 0, nullptr, 'l', "List of tests (-t X)", nullptr }, | |
28ab034a JG |
917 | { "validation-file", |
918 | 'o', | |
919 | POPT_ARG_STRING, | |
920 | &test_validation_output_file_path, | |
921 | 0, | |
922 | "Test case output", | |
cd9adb8b JG |
923 | nullptr }, |
924 | POPT_AUTOHELP{ nullptr, 0, 0, nullptr, 0, nullptr, nullptr } | |
a0b1f42c | 925 | }; |
d40c2620 | 926 | const struct test_case *test_case; |
a0b1f42c | 927 | |
cd9adb8b | 928 | optCon = poptGetContext(nullptr, argc, argv, optionsTable, 0); |
a0b1f42c JD |
929 | |
930 | if (argc < 2) { | |
931 | poptPrintUsage(optCon, stderr, 0); | |
932 | ret = -1; | |
933 | goto end; | |
934 | } | |
935 | ||
936 | ret = 0; | |
937 | ||
938 | while ((c = poptGetNextOpt(optCon)) >= 0) { | |
d40c2620 | 939 | switch (c) { |
a0b1f42c JD |
940 | case 'l': |
941 | print_list(); | |
942 | goto end; | |
943 | } | |
944 | } | |
945 | ||
d40c2620 JG |
946 | if (!test_validation_output_file_path) { |
947 | fprintf(stderr, "A test validation file path is required (--validation-file/-o)\n"); | |
948 | ret = -1; | |
949 | goto end; | |
950 | } | |
951 | ||
952 | test_validation_output_file = fopen(test_validation_output_file_path, "w+"); | |
953 | if (!test_validation_output_file) { | |
954 | PERROR("Failed to create test validation output file at '%s'", | |
28ab034a | 955 | test_validation_output_file_path); |
d40c2620 JG |
956 | ret = -1; |
957 | goto end; | |
958 | } | |
959 | ||
a0b1f42c JD |
960 | open_lim.rlim_cur = MAX_FDS + MIN_NR_FDS; |
961 | open_lim.rlim_max = MAX_FDS + MIN_NR_FDS; | |
962 | ||
963 | ret = setrlimit(RLIMIT_NOFILE, &open_lim); | |
964 | if (ret < 0) { | |
d40c2620 | 965 | PERROR("setrlimit"); |
a0b1f42c JD |
966 | goto end; |
967 | } | |
968 | ||
969 | /* | |
970 | * Some tests might segfault, but we need the getpid() to be output | |
d40c2620 JG |
971 | * for the validation, disabling the buffering on the validation file |
972 | * works. | |
a0b1f42c | 973 | */ |
cd9adb8b | 974 | setbuf(test_validation_output_file, nullptr); |
a0b1f42c JD |
975 | wait_fd = STDIN_FILENO; |
976 | ||
d40c2620 JG |
977 | /* Test case id is 1-based. */ |
978 | if (test < 1 || test > ARRAY_SIZE(test_cases)) { | |
a0b1f42c JD |
979 | poptPrintUsage(optCon, stderr, 0); |
980 | ret = -1; | |
a0b1f42c JD |
981 | } |
982 | ||
d40c2620 JG |
983 | test_case = &test_cases[test - 1]; |
984 | ||
985 | timeout = test_case->timeout; | |
986 | if (!test_case->produces_validation_info) { | |
987 | /* | |
988 | * All test cases need to provide, at minimum, the pid of the | |
989 | * test application. | |
990 | */ | |
991 | ret = fprintf(test_validation_output_file, "{ \"pid\": %i }", getpid()); | |
992 | if (ret < 0) { | |
993 | PERROR("Failed to write application pid to test validation file"); | |
994 | goto end; | |
995 | } | |
996 | } | |
997 | ||
998 | test_case->run(test_validation_output_file); | |
999 | ||
a0b1f42c | 1000 | end: |
d40c2620 JG |
1001 | if (test_validation_output_file) { |
1002 | const int close_ret = fclose(test_validation_output_file); | |
1003 | ||
1004 | if (close_ret) { | |
1005 | PERROR("Failed to close test output file"); | |
1006 | } | |
1007 | } | |
a0b1f42c JD |
1008 | poptFreeContext(optCon); |
1009 | return ret; | |
1010 | } |