Loading...
1// SPDX-License-Identifier: GPL-2.0
2
3#define _GNU_SOURCE
4#include <errno.h>
5#include <fcntl.h>
6#include <linux/kernel.h>
7#include <limits.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <syscall.h>
13#include <unistd.h>
14#include <sys/resource.h>
15#include <linux/close_range.h>
16
17#include "../kselftest_harness.h"
18#include "../clone3/clone3_selftests.h"
19
20
21#ifndef F_LINUX_SPECIFIC_BASE
22#define F_LINUX_SPECIFIC_BASE 1024
23#endif
24
25#ifndef F_DUPFD_QUERY
26#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
27#endif
28
29#ifndef F_CREATED_QUERY
30#define F_CREATED_QUERY (F_LINUX_SPECIFIC_BASE + 4)
31#endif
32
33static inline int sys_close_range(unsigned int fd, unsigned int max_fd,
34 unsigned int flags)
35{
36 return syscall(__NR_close_range, fd, max_fd, flags);
37}
38
39TEST(core_close_range)
40{
41 int i, ret;
42 int open_fds[101];
43
44 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
45 int fd;
46
47 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
48 ASSERT_GE(fd, 0) {
49 if (errno == ENOENT)
50 SKIP(return, "Skipping test since /dev/null does not exist");
51 }
52
53 open_fds[i] = fd;
54 }
55
56 EXPECT_EQ(-1, sys_close_range(open_fds[0], open_fds[100], -1)) {
57 if (errno == ENOSYS)
58 SKIP(return, "close_range() syscall not supported");
59 }
60
61 for (i = 0; i < 100; i++) {
62 ret = fcntl(open_fds[i], F_DUPFD_QUERY, open_fds[i + 1]);
63 if (ret < 0) {
64 EXPECT_EQ(errno, EINVAL);
65 } else {
66 EXPECT_EQ(ret, 0);
67 }
68 }
69
70 EXPECT_EQ(0, sys_close_range(open_fds[0], open_fds[50], 0));
71
72 for (i = 0; i <= 50; i++)
73 EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));
74
75 for (i = 51; i <= 100; i++)
76 EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);
77
78 /* create a couple of gaps */
79 close(57);
80 close(78);
81 close(81);
82 close(82);
83 close(84);
84 close(90);
85
86 EXPECT_EQ(0, sys_close_range(open_fds[51], open_fds[92], 0));
87
88 for (i = 51; i <= 92; i++)
89 EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));
90
91 for (i = 93; i <= 100; i++)
92 EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);
93
94 /* test that the kernel caps and still closes all fds */
95 EXPECT_EQ(0, sys_close_range(open_fds[93], open_fds[99], 0));
96
97 for (i = 93; i <= 99; i++)
98 EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));
99
100 EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);
101
102 EXPECT_EQ(0, sys_close_range(open_fds[100], open_fds[100], 0));
103
104 EXPECT_EQ(-1, fcntl(open_fds[100], F_GETFL));
105}
106
107TEST(close_range_unshare)
108{
109 int i, ret, status;
110 pid_t pid;
111 int open_fds[101];
112 struct __clone_args args = {
113 .flags = CLONE_FILES,
114 .exit_signal = SIGCHLD,
115 };
116
117 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
118 int fd;
119
120 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
121 ASSERT_GE(fd, 0) {
122 if (errno == ENOENT)
123 SKIP(return, "Skipping test since /dev/null does not exist");
124 }
125
126 open_fds[i] = fd;
127 }
128
129 pid = sys_clone3(&args, sizeof(args));
130 ASSERT_GE(pid, 0);
131
132 if (pid == 0) {
133 ret = sys_close_range(open_fds[0], open_fds[50],
134 CLOSE_RANGE_UNSHARE);
135 if (ret)
136 exit(EXIT_FAILURE);
137
138 for (i = 0; i <= 50; i++)
139 if (fcntl(open_fds[i], F_GETFL) != -1)
140 exit(EXIT_FAILURE);
141
142 for (i = 51; i <= 100; i++)
143 if (fcntl(open_fds[i], F_GETFL) == -1)
144 exit(EXIT_FAILURE);
145
146 /* create a couple of gaps */
147 close(57);
148 close(78);
149 close(81);
150 close(82);
151 close(84);
152 close(90);
153
154 ret = sys_close_range(open_fds[51], open_fds[92],
155 CLOSE_RANGE_UNSHARE);
156 if (ret)
157 exit(EXIT_FAILURE);
158
159 for (i = 51; i <= 92; i++)
160 if (fcntl(open_fds[i], F_GETFL) != -1)
161 exit(EXIT_FAILURE);
162
163 for (i = 93; i <= 100; i++)
164 if (fcntl(open_fds[i], F_GETFL) == -1)
165 exit(EXIT_FAILURE);
166
167 /* test that the kernel caps and still closes all fds */
168 ret = sys_close_range(open_fds[93], open_fds[99],
169 CLOSE_RANGE_UNSHARE);
170 if (ret)
171 exit(EXIT_FAILURE);
172
173 for (i = 93; i <= 99; i++)
174 if (fcntl(open_fds[i], F_GETFL) != -1)
175 exit(EXIT_FAILURE);
176
177 if (fcntl(open_fds[100], F_GETFL) == -1)
178 exit(EXIT_FAILURE);
179
180 ret = sys_close_range(open_fds[100], open_fds[100],
181 CLOSE_RANGE_UNSHARE);
182 if (ret)
183 exit(EXIT_FAILURE);
184
185 if (fcntl(open_fds[100], F_GETFL) != -1)
186 exit(EXIT_FAILURE);
187
188 exit(EXIT_SUCCESS);
189 }
190
191 EXPECT_EQ(waitpid(pid, &status, 0), pid);
192 EXPECT_EQ(true, WIFEXITED(status));
193 EXPECT_EQ(0, WEXITSTATUS(status));
194}
195
196TEST(close_range_unshare_capped)
197{
198 int i, ret, status;
199 pid_t pid;
200 int open_fds[101];
201 struct __clone_args args = {
202 .flags = CLONE_FILES,
203 .exit_signal = SIGCHLD,
204 };
205
206 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
207 int fd;
208
209 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
210 ASSERT_GE(fd, 0) {
211 if (errno == ENOENT)
212 SKIP(return, "Skipping test since /dev/null does not exist");
213 }
214
215 open_fds[i] = fd;
216 }
217
218 pid = sys_clone3(&args, sizeof(args));
219 ASSERT_GE(pid, 0);
220
221 if (pid == 0) {
222 ret = sys_close_range(open_fds[0], UINT_MAX,
223 CLOSE_RANGE_UNSHARE);
224 if (ret)
225 exit(EXIT_FAILURE);
226
227 for (i = 0; i <= 100; i++)
228 if (fcntl(open_fds[i], F_GETFL) != -1)
229 exit(EXIT_FAILURE);
230
231 exit(EXIT_SUCCESS);
232 }
233
234 EXPECT_EQ(waitpid(pid, &status, 0), pid);
235 EXPECT_EQ(true, WIFEXITED(status));
236 EXPECT_EQ(0, WEXITSTATUS(status));
237}
238
239TEST(close_range_cloexec)
240{
241 int i, ret;
242 int open_fds[101];
243 struct rlimit rlimit;
244
245 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
246 int fd;
247
248 fd = open("/dev/null", O_RDONLY);
249 ASSERT_GE(fd, 0) {
250 if (errno == ENOENT)
251 SKIP(return, "Skipping test since /dev/null does not exist");
252 }
253
254 open_fds[i] = fd;
255 }
256
257 ret = sys_close_range(1000, 1000, CLOSE_RANGE_CLOEXEC);
258 if (ret < 0) {
259 if (errno == ENOSYS)
260 SKIP(return, "close_range() syscall not supported");
261 if (errno == EINVAL)
262 SKIP(return, "close_range() doesn't support CLOSE_RANGE_CLOEXEC");
263 }
264
265 /* Ensure the FD_CLOEXEC bit is set also with a resource limit in place. */
266 ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit));
267 rlimit.rlim_cur = 25;
268 ASSERT_EQ(0, setrlimit(RLIMIT_NOFILE, &rlimit));
269
270 /* Set close-on-exec for two ranges: [0-50] and [75-100]. */
271 ret = sys_close_range(open_fds[0], open_fds[50], CLOSE_RANGE_CLOEXEC);
272 ASSERT_EQ(0, ret);
273 ret = sys_close_range(open_fds[75], open_fds[100], CLOSE_RANGE_CLOEXEC);
274 ASSERT_EQ(0, ret);
275
276 for (i = 0; i <= 50; i++) {
277 int flags = fcntl(open_fds[i], F_GETFD);
278
279 EXPECT_GT(flags, -1);
280 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
281 }
282
283 for (i = 51; i <= 74; i++) {
284 int flags = fcntl(open_fds[i], F_GETFD);
285
286 EXPECT_GT(flags, -1);
287 EXPECT_EQ(flags & FD_CLOEXEC, 0);
288 }
289
290 for (i = 75; i <= 100; i++) {
291 int flags = fcntl(open_fds[i], F_GETFD);
292
293 EXPECT_GT(flags, -1);
294 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
295 }
296
297 /* Test a common pattern. */
298 ret = sys_close_range(3, UINT_MAX, CLOSE_RANGE_CLOEXEC);
299 for (i = 0; i <= 100; i++) {
300 int flags = fcntl(open_fds[i], F_GETFD);
301
302 EXPECT_GT(flags, -1);
303 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
304 }
305}
306
307TEST(close_range_cloexec_unshare)
308{
309 int i, ret;
310 int open_fds[101];
311 struct rlimit rlimit;
312
313 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
314 int fd;
315
316 fd = open("/dev/null", O_RDONLY);
317 ASSERT_GE(fd, 0) {
318 if (errno == ENOENT)
319 SKIP(return, "Skipping test since /dev/null does not exist");
320 }
321
322 open_fds[i] = fd;
323 }
324
325 ret = sys_close_range(1000, 1000, CLOSE_RANGE_CLOEXEC);
326 if (ret < 0) {
327 if (errno == ENOSYS)
328 SKIP(return, "close_range() syscall not supported");
329 if (errno == EINVAL)
330 SKIP(return, "close_range() doesn't support CLOSE_RANGE_CLOEXEC");
331 }
332
333 /* Ensure the FD_CLOEXEC bit is set also with a resource limit in place. */
334 ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit));
335 rlimit.rlim_cur = 25;
336 ASSERT_EQ(0, setrlimit(RLIMIT_NOFILE, &rlimit));
337
338 /* Set close-on-exec for two ranges: [0-50] and [75-100]. */
339 ret = sys_close_range(open_fds[0], open_fds[50],
340 CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
341 ASSERT_EQ(0, ret);
342 ret = sys_close_range(open_fds[75], open_fds[100],
343 CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
344 ASSERT_EQ(0, ret);
345
346 for (i = 0; i <= 50; i++) {
347 int flags = fcntl(open_fds[i], F_GETFD);
348
349 EXPECT_GT(flags, -1);
350 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
351 }
352
353 for (i = 51; i <= 74; i++) {
354 int flags = fcntl(open_fds[i], F_GETFD);
355
356 EXPECT_GT(flags, -1);
357 EXPECT_EQ(flags & FD_CLOEXEC, 0);
358 }
359
360 for (i = 75; i <= 100; i++) {
361 int flags = fcntl(open_fds[i], F_GETFD);
362
363 EXPECT_GT(flags, -1);
364 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
365 }
366
367 /* Test a common pattern. */
368 ret = sys_close_range(3, UINT_MAX,
369 CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
370 for (i = 0; i <= 100; i++) {
371 int flags = fcntl(open_fds[i], F_GETFD);
372
373 EXPECT_GT(flags, -1);
374 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
375 }
376}
377
378/*
379 * Regression test for syzbot+96cfd2b22b3213646a93@syzkaller.appspotmail.com
380 */
381TEST(close_range_cloexec_syzbot)
382{
383 int fd1, fd2, fd3, fd4, flags, ret, status;
384 pid_t pid;
385 struct __clone_args args = {
386 .flags = CLONE_FILES,
387 .exit_signal = SIGCHLD,
388 };
389
390 /* Create a huge gap in the fd table. */
391 fd1 = open("/dev/null", O_RDWR);
392 EXPECT_GT(fd1, 0);
393
394 fd2 = dup2(fd1, 1000);
395 EXPECT_GT(fd2, 0);
396
397 flags = fcntl(fd1, F_DUPFD_QUERY, fd2);
398 if (flags < 0) {
399 EXPECT_EQ(errno, EINVAL);
400 } else {
401 EXPECT_EQ(flags, 1);
402 }
403
404 pid = sys_clone3(&args, sizeof(args));
405 ASSERT_GE(pid, 0);
406
407 if (pid == 0) {
408 ret = sys_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC);
409 if (ret)
410 exit(EXIT_FAILURE);
411
412 /*
413 * We now have a private file descriptor table and all
414 * our open fds should still be open but made
415 * close-on-exec.
416 */
417 flags = fcntl(fd1, F_GETFD);
418 EXPECT_GT(flags, -1);
419 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
420
421 flags = fcntl(fd2, F_GETFD);
422 EXPECT_GT(flags, -1);
423 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
424
425 fd3 = dup2(fd1, 42);
426 EXPECT_GT(fd3, 0);
427
428 flags = fcntl(fd1, F_DUPFD_QUERY, fd3);
429 if (flags < 0) {
430 EXPECT_EQ(errno, EINVAL);
431 } else {
432 EXPECT_EQ(flags, 1);
433 }
434
435
436
437 /*
438 * Duplicating the file descriptor must remove the
439 * FD_CLOEXEC flag.
440 */
441 flags = fcntl(fd3, F_GETFD);
442 EXPECT_GT(flags, -1);
443 EXPECT_EQ(flags & FD_CLOEXEC, 0);
444
445 exit(EXIT_SUCCESS);
446 }
447
448 EXPECT_EQ(waitpid(pid, &status, 0), pid);
449 EXPECT_EQ(true, WIFEXITED(status));
450 EXPECT_EQ(0, WEXITSTATUS(status));
451
452 /*
453 * We had a shared file descriptor table before along with requesting
454 * close-on-exec so the original fds must not be close-on-exec.
455 */
456 flags = fcntl(fd1, F_GETFD);
457 EXPECT_GT(flags, -1);
458 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
459
460 flags = fcntl(fd2, F_GETFD);
461 EXPECT_GT(flags, -1);
462 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
463
464 fd3 = dup2(fd1, 42);
465 EXPECT_GT(fd3, 0);
466
467 flags = fcntl(fd1, F_DUPFD_QUERY, fd3);
468 if (flags < 0) {
469 EXPECT_EQ(errno, EINVAL);
470 } else {
471 EXPECT_EQ(flags, 1);
472 }
473
474 fd4 = open("/dev/null", O_RDWR);
475 EXPECT_GT(fd4, 0);
476
477 /* Same inode, different file pointers. */
478 flags = fcntl(fd1, F_DUPFD_QUERY, fd4);
479 if (flags < 0) {
480 EXPECT_EQ(errno, EINVAL);
481 } else {
482 EXPECT_EQ(flags, 0);
483 }
484
485 flags = fcntl(fd3, F_GETFD);
486 EXPECT_GT(flags, -1);
487 EXPECT_EQ(flags & FD_CLOEXEC, 0);
488
489 EXPECT_EQ(close(fd1), 0);
490 EXPECT_EQ(close(fd2), 0);
491 EXPECT_EQ(close(fd3), 0);
492 EXPECT_EQ(close(fd4), 0);
493}
494
495/*
496 * Regression test for syzbot+96cfd2b22b3213646a93@syzkaller.appspotmail.com
497 */
498TEST(close_range_cloexec_unshare_syzbot)
499{
500 int i, fd1, fd2, fd3, flags, ret, status;
501 pid_t pid;
502 struct __clone_args args = {
503 .flags = CLONE_FILES,
504 .exit_signal = SIGCHLD,
505 };
506
507 /*
508 * Create a huge gap in the fd table. When we now call
509 * CLOSE_RANGE_UNSHARE with a shared fd table and and with ~0U as upper
510 * bound the kernel will only copy up to fd1 file descriptors into the
511 * new fd table. If the kernel is buggy and doesn't handle
512 * CLOSE_RANGE_CLOEXEC correctly it will not have copied all file
513 * descriptors and we will oops!
514 *
515 * On a buggy kernel this should immediately oops. But let's loop just
516 * to be sure.
517 */
518 fd1 = open("/dev/null", O_RDWR);
519 EXPECT_GT(fd1, 0);
520
521 fd2 = dup2(fd1, 1000);
522 EXPECT_GT(fd2, 0);
523
524 for (i = 0; i < 100; i++) {
525
526 pid = sys_clone3(&args, sizeof(args));
527 ASSERT_GE(pid, 0);
528
529 if (pid == 0) {
530 ret = sys_close_range(3, ~0U, CLOSE_RANGE_UNSHARE |
531 CLOSE_RANGE_CLOEXEC);
532 if (ret)
533 exit(EXIT_FAILURE);
534
535 /*
536 * We now have a private file descriptor table and all
537 * our open fds should still be open but made
538 * close-on-exec.
539 */
540 flags = fcntl(fd1, F_GETFD);
541 EXPECT_GT(flags, -1);
542 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
543
544 flags = fcntl(fd2, F_GETFD);
545 EXPECT_GT(flags, -1);
546 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
547
548 fd3 = dup2(fd1, 42);
549 EXPECT_GT(fd3, 0);
550
551 /*
552 * Duplicating the file descriptor must remove the
553 * FD_CLOEXEC flag.
554 */
555 flags = fcntl(fd3, F_GETFD);
556 EXPECT_GT(flags, -1);
557 EXPECT_EQ(flags & FD_CLOEXEC, 0);
558
559 EXPECT_EQ(close(fd1), 0);
560 EXPECT_EQ(close(fd2), 0);
561 EXPECT_EQ(close(fd3), 0);
562
563 exit(EXIT_SUCCESS);
564 }
565
566 EXPECT_EQ(waitpid(pid, &status, 0), pid);
567 EXPECT_EQ(true, WIFEXITED(status));
568 EXPECT_EQ(0, WEXITSTATUS(status));
569 }
570
571 /*
572 * We created a private file descriptor table before along with
573 * requesting close-on-exec so the original fds must not be
574 * close-on-exec.
575 */
576 flags = fcntl(fd1, F_GETFD);
577 EXPECT_GT(flags, -1);
578 EXPECT_EQ(flags & FD_CLOEXEC, 0);
579
580 flags = fcntl(fd2, F_GETFD);
581 EXPECT_GT(flags, -1);
582 EXPECT_EQ(flags & FD_CLOEXEC, 0);
583
584 fd3 = dup2(fd1, 42);
585 EXPECT_GT(fd3, 0);
586
587 flags = fcntl(fd3, F_GETFD);
588 EXPECT_GT(flags, -1);
589 EXPECT_EQ(flags & FD_CLOEXEC, 0);
590
591 EXPECT_EQ(close(fd1), 0);
592 EXPECT_EQ(close(fd2), 0);
593 EXPECT_EQ(close(fd3), 0);
594}
595
596TEST(close_range_bitmap_corruption)
597{
598 pid_t pid;
599 int status;
600 struct __clone_args args = {
601 .flags = CLONE_FILES,
602 .exit_signal = SIGCHLD,
603 };
604
605 /* get the first 128 descriptors open */
606 for (int i = 2; i < 128; i++)
607 EXPECT_GE(dup2(0, i), 0);
608
609 /* get descriptor table shared */
610 pid = sys_clone3(&args, sizeof(args));
611 ASSERT_GE(pid, 0);
612
613 if (pid == 0) {
614 /* unshare and truncate descriptor table down to 64 */
615 if (sys_close_range(64, ~0U, CLOSE_RANGE_UNSHARE))
616 exit(EXIT_FAILURE);
617
618 ASSERT_EQ(fcntl(64, F_GETFD), -1);
619 /* ... and verify that the range 64..127 is not
620 stuck "fully used" according to secondary bitmap */
621 EXPECT_EQ(dup(0), 64)
622 exit(EXIT_FAILURE);
623 exit(EXIT_SUCCESS);
624 }
625
626 EXPECT_EQ(waitpid(pid, &status, 0), pid);
627 EXPECT_EQ(true, WIFEXITED(status));
628 EXPECT_EQ(0, WEXITSTATUS(status));
629}
630
631TEST(fcntl_created)
632{
633 for (int i = 0; i < 101; i++) {
634 int fd;
635 char path[PATH_MAX];
636
637 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
638 ASSERT_GE(fd, 0) {
639 if (errno == ENOENT)
640 SKIP(return,
641 "Skipping test since /dev/null does not exist");
642 }
643
644 /* We didn't create "/dev/null". */
645 EXPECT_EQ(fcntl(fd, F_CREATED_QUERY, 0), 0);
646 close(fd);
647
648 sprintf(path, "aaaa_%d", i);
649 fd = open(path, O_CREAT | O_RDONLY | O_CLOEXEC, 0600);
650 ASSERT_GE(fd, 0);
651
652 /* We created "aaaa_%d". */
653 EXPECT_EQ(fcntl(fd, F_CREATED_QUERY, 0), 1);
654 close(fd);
655
656 fd = open(path, O_RDONLY | O_CLOEXEC);
657 ASSERT_GE(fd, 0);
658
659 /* We're opening it again, so no positive creation check. */
660 EXPECT_EQ(fcntl(fd, F_CREATED_QUERY, 0), 0);
661 close(fd);
662 unlink(path);
663 }
664}
665
666TEST_HARNESS_MAIN
1// SPDX-License-Identifier: GPL-2.0
2
3#define _GNU_SOURCE
4#include <errno.h>
5#include <fcntl.h>
6#include <linux/kernel.h>
7#include <limits.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <syscall.h>
13#include <unistd.h>
14#include <sys/resource.h>
15#include <linux/close_range.h>
16
17#include "../kselftest_harness.h"
18#include "../clone3/clone3_selftests.h"
19
20static inline int sys_close_range(unsigned int fd, unsigned int max_fd,
21 unsigned int flags)
22{
23 return syscall(__NR_close_range, fd, max_fd, flags);
24}
25
26TEST(core_close_range)
27{
28 int i, ret;
29 int open_fds[101];
30
31 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
32 int fd;
33
34 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
35 ASSERT_GE(fd, 0) {
36 if (errno == ENOENT)
37 SKIP(return, "Skipping test since /dev/null does not exist");
38 }
39
40 open_fds[i] = fd;
41 }
42
43 EXPECT_EQ(-1, sys_close_range(open_fds[0], open_fds[100], -1)) {
44 if (errno == ENOSYS)
45 SKIP(return, "close_range() syscall not supported");
46 }
47
48 EXPECT_EQ(0, sys_close_range(open_fds[0], open_fds[50], 0));
49
50 for (i = 0; i <= 50; i++)
51 EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));
52
53 for (i = 51; i <= 100; i++)
54 EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);
55
56 /* create a couple of gaps */
57 close(57);
58 close(78);
59 close(81);
60 close(82);
61 close(84);
62 close(90);
63
64 EXPECT_EQ(0, sys_close_range(open_fds[51], open_fds[92], 0));
65
66 for (i = 51; i <= 92; i++)
67 EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));
68
69 for (i = 93; i <= 100; i++)
70 EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);
71
72 /* test that the kernel caps and still closes all fds */
73 EXPECT_EQ(0, sys_close_range(open_fds[93], open_fds[99], 0));
74
75 for (i = 93; i <= 99; i++)
76 EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));
77
78 EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);
79
80 EXPECT_EQ(0, sys_close_range(open_fds[100], open_fds[100], 0));
81
82 EXPECT_EQ(-1, fcntl(open_fds[100], F_GETFL));
83}
84
85TEST(close_range_unshare)
86{
87 int i, ret, status;
88 pid_t pid;
89 int open_fds[101];
90 struct __clone_args args = {
91 .flags = CLONE_FILES,
92 .exit_signal = SIGCHLD,
93 };
94
95 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
96 int fd;
97
98 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
99 ASSERT_GE(fd, 0) {
100 if (errno == ENOENT)
101 SKIP(return, "Skipping test since /dev/null does not exist");
102 }
103
104 open_fds[i] = fd;
105 }
106
107 pid = sys_clone3(&args, sizeof(args));
108 ASSERT_GE(pid, 0);
109
110 if (pid == 0) {
111 ret = sys_close_range(open_fds[0], open_fds[50],
112 CLOSE_RANGE_UNSHARE);
113 if (ret)
114 exit(EXIT_FAILURE);
115
116 for (i = 0; i <= 50; i++)
117 if (fcntl(open_fds[i], F_GETFL) != -1)
118 exit(EXIT_FAILURE);
119
120 for (i = 51; i <= 100; i++)
121 if (fcntl(open_fds[i], F_GETFL) == -1)
122 exit(EXIT_FAILURE);
123
124 /* create a couple of gaps */
125 close(57);
126 close(78);
127 close(81);
128 close(82);
129 close(84);
130 close(90);
131
132 ret = sys_close_range(open_fds[51], open_fds[92],
133 CLOSE_RANGE_UNSHARE);
134 if (ret)
135 exit(EXIT_FAILURE);
136
137 for (i = 51; i <= 92; i++)
138 if (fcntl(open_fds[i], F_GETFL) != -1)
139 exit(EXIT_FAILURE);
140
141 for (i = 93; i <= 100; i++)
142 if (fcntl(open_fds[i], F_GETFL) == -1)
143 exit(EXIT_FAILURE);
144
145 /* test that the kernel caps and still closes all fds */
146 ret = sys_close_range(open_fds[93], open_fds[99],
147 CLOSE_RANGE_UNSHARE);
148 if (ret)
149 exit(EXIT_FAILURE);
150
151 for (i = 93; i <= 99; i++)
152 if (fcntl(open_fds[i], F_GETFL) != -1)
153 exit(EXIT_FAILURE);
154
155 if (fcntl(open_fds[100], F_GETFL) == -1)
156 exit(EXIT_FAILURE);
157
158 ret = sys_close_range(open_fds[100], open_fds[100],
159 CLOSE_RANGE_UNSHARE);
160 if (ret)
161 exit(EXIT_FAILURE);
162
163 if (fcntl(open_fds[100], F_GETFL) != -1)
164 exit(EXIT_FAILURE);
165
166 exit(EXIT_SUCCESS);
167 }
168
169 EXPECT_EQ(waitpid(pid, &status, 0), pid);
170 EXPECT_EQ(true, WIFEXITED(status));
171 EXPECT_EQ(0, WEXITSTATUS(status));
172}
173
174TEST(close_range_unshare_capped)
175{
176 int i, ret, status;
177 pid_t pid;
178 int open_fds[101];
179 struct __clone_args args = {
180 .flags = CLONE_FILES,
181 .exit_signal = SIGCHLD,
182 };
183
184 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
185 int fd;
186
187 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
188 ASSERT_GE(fd, 0) {
189 if (errno == ENOENT)
190 SKIP(return, "Skipping test since /dev/null does not exist");
191 }
192
193 open_fds[i] = fd;
194 }
195
196 pid = sys_clone3(&args, sizeof(args));
197 ASSERT_GE(pid, 0);
198
199 if (pid == 0) {
200 ret = sys_close_range(open_fds[0], UINT_MAX,
201 CLOSE_RANGE_UNSHARE);
202 if (ret)
203 exit(EXIT_FAILURE);
204
205 for (i = 0; i <= 100; i++)
206 if (fcntl(open_fds[i], F_GETFL) != -1)
207 exit(EXIT_FAILURE);
208
209 exit(EXIT_SUCCESS);
210 }
211
212 EXPECT_EQ(waitpid(pid, &status, 0), pid);
213 EXPECT_EQ(true, WIFEXITED(status));
214 EXPECT_EQ(0, WEXITSTATUS(status));
215}
216
217TEST(close_range_cloexec)
218{
219 int i, ret;
220 int open_fds[101];
221 struct rlimit rlimit;
222
223 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
224 int fd;
225
226 fd = open("/dev/null", O_RDONLY);
227 ASSERT_GE(fd, 0) {
228 if (errno == ENOENT)
229 SKIP(return, "Skipping test since /dev/null does not exist");
230 }
231
232 open_fds[i] = fd;
233 }
234
235 ret = sys_close_range(1000, 1000, CLOSE_RANGE_CLOEXEC);
236 if (ret < 0) {
237 if (errno == ENOSYS)
238 SKIP(return, "close_range() syscall not supported");
239 if (errno == EINVAL)
240 SKIP(return, "close_range() doesn't support CLOSE_RANGE_CLOEXEC");
241 }
242
243 /* Ensure the FD_CLOEXEC bit is set also with a resource limit in place. */
244 ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit));
245 rlimit.rlim_cur = 25;
246 ASSERT_EQ(0, setrlimit(RLIMIT_NOFILE, &rlimit));
247
248 /* Set close-on-exec for two ranges: [0-50] and [75-100]. */
249 ret = sys_close_range(open_fds[0], open_fds[50], CLOSE_RANGE_CLOEXEC);
250 ASSERT_EQ(0, ret);
251 ret = sys_close_range(open_fds[75], open_fds[100], CLOSE_RANGE_CLOEXEC);
252 ASSERT_EQ(0, ret);
253
254 for (i = 0; i <= 50; i++) {
255 int flags = fcntl(open_fds[i], F_GETFD);
256
257 EXPECT_GT(flags, -1);
258 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
259 }
260
261 for (i = 51; i <= 74; i++) {
262 int flags = fcntl(open_fds[i], F_GETFD);
263
264 EXPECT_GT(flags, -1);
265 EXPECT_EQ(flags & FD_CLOEXEC, 0);
266 }
267
268 for (i = 75; i <= 100; i++) {
269 int flags = fcntl(open_fds[i], F_GETFD);
270
271 EXPECT_GT(flags, -1);
272 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
273 }
274
275 /* Test a common pattern. */
276 ret = sys_close_range(3, UINT_MAX, CLOSE_RANGE_CLOEXEC);
277 for (i = 0; i <= 100; i++) {
278 int flags = fcntl(open_fds[i], F_GETFD);
279
280 EXPECT_GT(flags, -1);
281 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
282 }
283}
284
285TEST(close_range_cloexec_unshare)
286{
287 int i, ret;
288 int open_fds[101];
289 struct rlimit rlimit;
290
291 for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
292 int fd;
293
294 fd = open("/dev/null", O_RDONLY);
295 ASSERT_GE(fd, 0) {
296 if (errno == ENOENT)
297 SKIP(return, "Skipping test since /dev/null does not exist");
298 }
299
300 open_fds[i] = fd;
301 }
302
303 ret = sys_close_range(1000, 1000, CLOSE_RANGE_CLOEXEC);
304 if (ret < 0) {
305 if (errno == ENOSYS)
306 SKIP(return, "close_range() syscall not supported");
307 if (errno == EINVAL)
308 SKIP(return, "close_range() doesn't support CLOSE_RANGE_CLOEXEC");
309 }
310
311 /* Ensure the FD_CLOEXEC bit is set also with a resource limit in place. */
312 ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit));
313 rlimit.rlim_cur = 25;
314 ASSERT_EQ(0, setrlimit(RLIMIT_NOFILE, &rlimit));
315
316 /* Set close-on-exec for two ranges: [0-50] and [75-100]. */
317 ret = sys_close_range(open_fds[0], open_fds[50],
318 CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
319 ASSERT_EQ(0, ret);
320 ret = sys_close_range(open_fds[75], open_fds[100],
321 CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
322 ASSERT_EQ(0, ret);
323
324 for (i = 0; i <= 50; i++) {
325 int flags = fcntl(open_fds[i], F_GETFD);
326
327 EXPECT_GT(flags, -1);
328 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
329 }
330
331 for (i = 51; i <= 74; i++) {
332 int flags = fcntl(open_fds[i], F_GETFD);
333
334 EXPECT_GT(flags, -1);
335 EXPECT_EQ(flags & FD_CLOEXEC, 0);
336 }
337
338 for (i = 75; i <= 100; i++) {
339 int flags = fcntl(open_fds[i], F_GETFD);
340
341 EXPECT_GT(flags, -1);
342 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
343 }
344
345 /* Test a common pattern. */
346 ret = sys_close_range(3, UINT_MAX,
347 CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
348 for (i = 0; i <= 100; i++) {
349 int flags = fcntl(open_fds[i], F_GETFD);
350
351 EXPECT_GT(flags, -1);
352 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
353 }
354}
355
356/*
357 * Regression test for syzbot+96cfd2b22b3213646a93@syzkaller.appspotmail.com
358 */
359TEST(close_range_cloexec_syzbot)
360{
361 int fd1, fd2, fd3, flags, ret, status;
362 pid_t pid;
363 struct __clone_args args = {
364 .flags = CLONE_FILES,
365 .exit_signal = SIGCHLD,
366 };
367
368 /* Create a huge gap in the fd table. */
369 fd1 = open("/dev/null", O_RDWR);
370 EXPECT_GT(fd1, 0);
371
372 fd2 = dup2(fd1, 1000);
373 EXPECT_GT(fd2, 0);
374
375 pid = sys_clone3(&args, sizeof(args));
376 ASSERT_GE(pid, 0);
377
378 if (pid == 0) {
379 ret = sys_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC);
380 if (ret)
381 exit(EXIT_FAILURE);
382
383 /*
384 * We now have a private file descriptor table and all
385 * our open fds should still be open but made
386 * close-on-exec.
387 */
388 flags = fcntl(fd1, F_GETFD);
389 EXPECT_GT(flags, -1);
390 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
391
392 flags = fcntl(fd2, F_GETFD);
393 EXPECT_GT(flags, -1);
394 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
395
396 fd3 = dup2(fd1, 42);
397 EXPECT_GT(fd3, 0);
398
399 /*
400 * Duplicating the file descriptor must remove the
401 * FD_CLOEXEC flag.
402 */
403 flags = fcntl(fd3, F_GETFD);
404 EXPECT_GT(flags, -1);
405 EXPECT_EQ(flags & FD_CLOEXEC, 0);
406
407 exit(EXIT_SUCCESS);
408 }
409
410 EXPECT_EQ(waitpid(pid, &status, 0), pid);
411 EXPECT_EQ(true, WIFEXITED(status));
412 EXPECT_EQ(0, WEXITSTATUS(status));
413
414 /*
415 * We had a shared file descriptor table before along with requesting
416 * close-on-exec so the original fds must not be close-on-exec.
417 */
418 flags = fcntl(fd1, F_GETFD);
419 EXPECT_GT(flags, -1);
420 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
421
422 flags = fcntl(fd2, F_GETFD);
423 EXPECT_GT(flags, -1);
424 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
425
426 fd3 = dup2(fd1, 42);
427 EXPECT_GT(fd3, 0);
428
429 flags = fcntl(fd3, F_GETFD);
430 EXPECT_GT(flags, -1);
431 EXPECT_EQ(flags & FD_CLOEXEC, 0);
432
433 EXPECT_EQ(close(fd1), 0);
434 EXPECT_EQ(close(fd2), 0);
435 EXPECT_EQ(close(fd3), 0);
436}
437
438/*
439 * Regression test for syzbot+96cfd2b22b3213646a93@syzkaller.appspotmail.com
440 */
441TEST(close_range_cloexec_unshare_syzbot)
442{
443 int i, fd1, fd2, fd3, flags, ret, status;
444 pid_t pid;
445 struct __clone_args args = {
446 .flags = CLONE_FILES,
447 .exit_signal = SIGCHLD,
448 };
449
450 /*
451 * Create a huge gap in the fd table. When we now call
452 * CLOSE_RANGE_UNSHARE with a shared fd table and and with ~0U as upper
453 * bound the kernel will only copy up to fd1 file descriptors into the
454 * new fd table. If the kernel is buggy and doesn't handle
455 * CLOSE_RANGE_CLOEXEC correctly it will not have copied all file
456 * descriptors and we will oops!
457 *
458 * On a buggy kernel this should immediately oops. But let's loop just
459 * to be sure.
460 */
461 fd1 = open("/dev/null", O_RDWR);
462 EXPECT_GT(fd1, 0);
463
464 fd2 = dup2(fd1, 1000);
465 EXPECT_GT(fd2, 0);
466
467 for (i = 0; i < 100; i++) {
468
469 pid = sys_clone3(&args, sizeof(args));
470 ASSERT_GE(pid, 0);
471
472 if (pid == 0) {
473 ret = sys_close_range(3, ~0U, CLOSE_RANGE_UNSHARE |
474 CLOSE_RANGE_CLOEXEC);
475 if (ret)
476 exit(EXIT_FAILURE);
477
478 /*
479 * We now have a private file descriptor table and all
480 * our open fds should still be open but made
481 * close-on-exec.
482 */
483 flags = fcntl(fd1, F_GETFD);
484 EXPECT_GT(flags, -1);
485 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
486
487 flags = fcntl(fd2, F_GETFD);
488 EXPECT_GT(flags, -1);
489 EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
490
491 fd3 = dup2(fd1, 42);
492 EXPECT_GT(fd3, 0);
493
494 /*
495 * Duplicating the file descriptor must remove the
496 * FD_CLOEXEC flag.
497 */
498 flags = fcntl(fd3, F_GETFD);
499 EXPECT_GT(flags, -1);
500 EXPECT_EQ(flags & FD_CLOEXEC, 0);
501
502 EXPECT_EQ(close(fd1), 0);
503 EXPECT_EQ(close(fd2), 0);
504 EXPECT_EQ(close(fd3), 0);
505
506 exit(EXIT_SUCCESS);
507 }
508
509 EXPECT_EQ(waitpid(pid, &status, 0), pid);
510 EXPECT_EQ(true, WIFEXITED(status));
511 EXPECT_EQ(0, WEXITSTATUS(status));
512 }
513
514 /*
515 * We created a private file descriptor table before along with
516 * requesting close-on-exec so the original fds must not be
517 * close-on-exec.
518 */
519 flags = fcntl(fd1, F_GETFD);
520 EXPECT_GT(flags, -1);
521 EXPECT_EQ(flags & FD_CLOEXEC, 0);
522
523 flags = fcntl(fd2, F_GETFD);
524 EXPECT_GT(flags, -1);
525 EXPECT_EQ(flags & FD_CLOEXEC, 0);
526
527 fd3 = dup2(fd1, 42);
528 EXPECT_GT(fd3, 0);
529
530 flags = fcntl(fd3, F_GETFD);
531 EXPECT_GT(flags, -1);
532 EXPECT_EQ(flags & FD_CLOEXEC, 0);
533
534 EXPECT_EQ(close(fd1), 0);
535 EXPECT_EQ(close(fd2), 0);
536 EXPECT_EQ(close(fd3), 0);
537}
538
539TEST_HARNESS_MAIN