]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-fd-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include <sys/eventfd.h>
7 #include "alloc-util.h"
8 #include "data-fd-util.h"
12 #include "memory-util.h"
13 #include "missing_syscall.h"
14 #include "mount-util.h"
15 #include "path-util.h"
16 #include "process-util.h"
17 #include "random-util.h"
18 #include "rlimit-util.h"
19 #include "seccomp-util.h"
20 #include "serialize.h"
21 #include "string-util.h"
23 #include "tmpfile-util.h"
27 char name0
[] = "/tmp/test-close-many.XXXXXX";
28 char name1
[] = "/tmp/test-close-many.XXXXXX";
29 char name2
[] = "/tmp/test-close-many.XXXXXX";
31 fds
[0] = mkostemp_safe(name0
);
32 fds
[1] = mkostemp_safe(name1
);
33 fds
[2] = mkostemp_safe(name2
);
37 assert_se(fcntl(fds
[0], F_GETFD
) == -1);
38 assert_se(fcntl(fds
[1], F_GETFD
) == -1);
39 assert_se(fcntl(fds
[2], F_GETFD
) >= 0);
49 char name
[] = "/tmp/test-test-close_nointr.XXXXXX";
52 fd
= mkostemp_safe(name
);
54 assert_se(close_nointr(fd
) >= 0);
55 assert_se(close_nointr(fd
) < 0);
61 _cleanup_close_pair_
int p
[2] = { -1, -1 };
62 _cleanup_close_
int a
, b
, c
;
64 assert_se(pipe2(p
, O_CLOEXEC
) >= 0);
65 assert_se((a
= fcntl(p
[0], F_DUPFD
, 3)) >= 0);
66 assert_se((b
= open("/dev/null", O_RDONLY
|O_CLOEXEC
)) >= 0);
67 assert_se((c
= fcntl(a
, F_DUPFD
, 3)) >= 0);
69 assert_se(same_fd(p
[0], p
[0]) > 0);
70 assert_se(same_fd(p
[1], p
[1]) > 0);
71 assert_se(same_fd(a
, a
) > 0);
72 assert_se(same_fd(b
, b
) > 0);
74 assert_se(same_fd(a
, p
[0]) > 0);
75 assert_se(same_fd(p
[0], a
) > 0);
76 assert_se(same_fd(c
, p
[0]) > 0);
77 assert_se(same_fd(p
[0], c
) > 0);
78 assert_se(same_fd(a
, c
) > 0);
79 assert_se(same_fd(c
, a
) > 0);
81 assert_se(same_fd(p
[0], p
[1]) == 0);
82 assert_se(same_fd(p
[1], p
[0]) == 0);
83 assert_se(same_fd(p
[0], b
) == 0);
84 assert_se(same_fd(b
, p
[0]) == 0);
85 assert_se(same_fd(p
[1], a
) == 0);
86 assert_se(same_fd(a
, p
[1]) == 0);
87 assert_se(same_fd(p
[1], b
) == 0);
88 assert_se(same_fd(b
, p
[1]) == 0);
90 assert_se(same_fd(a
, b
) == 0);
91 assert_se(same_fd(b
, a
) == 0);
94 TEST(open_serialization_fd
) {
95 _cleanup_close_
int fd
= -EBADF
;
97 fd
= open_serialization_fd("test");
100 assert_se(write(fd
, "test\n", 5) == 5);
103 TEST(fd_move_above_stdio
) {
104 int original_stdin
, new_fd
;
106 original_stdin
= fcntl(0, F_DUPFD
, 3);
107 assert_se(original_stdin
>= 3);
108 assert_se(close_nointr(0) != EBADF
);
110 new_fd
= open("/dev/null", O_RDONLY
);
111 assert_se(new_fd
== 0);
113 new_fd
= fd_move_above_stdio(new_fd
);
114 assert_se(new_fd
>= 3);
116 assert_se(dup(original_stdin
) == 0);
117 assert_se(close_nointr(original_stdin
) != EBADF
);
118 assert_se(close_nointr(new_fd
) != EBADF
);
121 TEST(rearrange_stdio
) {
125 r
= safe_fork("rearrange", FORK_WAIT
|FORK_LOG
, &pid
);
129 _cleanup_free_
char *path
= NULL
;
134 safe_close(STDERR_FILENO
); /* Let's close an fd < 2, to make it more interesting */
136 assert_se(rearrange_stdio(-1, -1, -1) >= 0);
138 assert_se(fd_get_path(STDIN_FILENO
, &path
) >= 0);
139 assert_se(path_equal(path
, "/dev/null"));
142 assert_se(fd_get_path(STDOUT_FILENO
, &path
) >= 0);
143 assert_se(path_equal(path
, "/dev/null"));
146 assert_se(fd_get_path(STDOUT_FILENO
, &path
) >= 0);
147 assert_se(path_equal(path
, "/dev/null"));
150 safe_close(STDIN_FILENO
);
151 safe_close(STDOUT_FILENO
);
152 safe_close(STDERR_FILENO
);
156 assert_se(pipe(pair
) >= 0);
157 assert_se(pair
[0] == 0);
158 assert_se(pair
[1] == 1);
159 assert_se(fd_move_above_stdio(0) == 3);
161 assert_se(open("/dev/full", O_WRONLY
|O_CLOEXEC
) == 0);
162 assert_se(acquire_data_fd("foobar", 6, 0) == 2);
164 assert_se(rearrange_stdio(2, 0, 1) >= 0);
166 assert_se(write(1, "x", 1) < 0 && errno
== ENOSPC
);
167 assert_se(write(2, "z", 1) == 1);
168 assert_se(read(3, buffer
, sizeof(buffer
)) == 1);
169 assert_se(buffer
[0] == 'z');
170 assert_se(read(0, buffer
, sizeof(buffer
)) == 6);
171 assert_se(memcmp(buffer
, "foobar", 6) == 0);
173 assert_se(rearrange_stdio(-1, 1, 2) >= 0);
174 assert_se(write(1, "a", 1) < 0 && errno
== ENOSPC
);
175 assert_se(write(2, "y", 1) == 1);
176 assert_se(read(3, buffer
, sizeof(buffer
)) == 1);
177 assert_se(buffer
[0] == 'y');
179 assert_se(fd_get_path(0, &path
) >= 0);
180 assert_se(path_equal(path
, "/dev/null"));
188 log_info("nr-open: %i", read_nr_open());
191 static size_t validate_fds(
198 /* Validates that fds in the specified array are one of the following three:
200 * 1. < 0 (test is skipped) or
201 * 2. opened (if 'opened' param is true) or
202 * 3. closed (if 'opened' param is false)
205 for (size_t i
= 0; i
< n_fds
; i
++) {
210 assert_se(fcntl(fds
[i
], F_GETFD
) >= 0);
212 assert_se(fcntl(fds
[i
], F_GETFD
) < 0 && errno
== EBADF
);
217 return c
; /* Return number of fds >= 0 in the array */
220 static void test_close_all_fds_inner(void) {
221 _cleanup_free_
int *fds
= NULL
, *keep
= NULL
;
222 size_t n_fds
, n_keep
;
225 log_info("/* %s */", __func__
);
227 rlimit_nofile_bump(-1);
229 max_fd
= get_max_fd();
230 assert_se(max_fd
> 10);
233 /* If the worst fallback is activated we need to iterate through all possible fds, hence,
234 * let's lower the limit a small bit, so that we don't run for too long. Yes, this undoes the
235 * rlimit_nofile_bump() call above partially. */
237 (void) setrlimit_closest(RLIMIT_NOFILE
, &(struct rlimit
) { 7000, 7000 });
241 /* Try to use 5000 fds, but when we can't bump the rlimit to make that happen use the whole limit minus 10 */
242 n_fds
= MIN(((size_t) max_fd
& ~1U) - 10U, 5000U);
243 assert_se((n_fds
& 1U) == 0U); /* make sure even number of fds */
245 /* Allocate the determined number of fds, always two at a time */
246 assert_se(fds
= new(int, n_fds
));
247 for (size_t i
= 0; i
< n_fds
; i
+= 2)
248 assert_se(pipe2(fds
+ i
, O_CLOEXEC
) >= 0);
250 /* Validate this worked */
251 assert_se(validate_fds(true, fds
, n_fds
) == n_fds
);
253 /* Randomized number of fds to keep, but at most every second */
254 n_keep
= (random_u64() % (n_fds
/ 2));
256 /* Now randomly select a number of fds from the array above to keep */
257 assert_se(keep
= new(int, n_keep
));
258 for (size_t k
= 0; k
< n_keep
; k
++) {
262 p
= random_u64() % n_fds
;
264 keep
[k
] = TAKE_FD(fds
[p
]);
270 /* Check that all fds from both arrays are still open, and test how many in each are >= 0 */
271 assert_se(validate_fds(true, fds
, n_fds
) == n_fds
- n_keep
);
272 assert_se(validate_fds(true, keep
, n_keep
) == n_keep
);
274 /* Close logging fd first, so that we don't confuse it by closing its fd */
276 log_set_open_when_needed(true);
278 /* Close all but the ones to keep */
279 assert_se(close_all_fds(keep
, n_keep
) >= 0);
281 assert_se(validate_fds(false, fds
, n_fds
) == n_fds
- n_keep
);
282 assert_se(validate_fds(true, keep
, n_keep
) == n_keep
);
284 /* Close everything else too! */
285 assert_se(close_all_fds(NULL
, 0) >= 0);
287 assert_se(validate_fds(false, fds
, n_fds
) == n_fds
- n_keep
);
288 assert_se(validate_fds(false, keep
, n_keep
) == n_keep
);
290 log_set_open_when_needed(false);
294 static int seccomp_prohibit_close_range(void) {
295 #if HAVE_SECCOMP && defined(__SNR_close_range)
296 _cleanup_(seccomp_releasep
) scmp_filter_ctx seccomp
= NULL
;
299 r
= seccomp_init_for_arch(&seccomp
, SCMP_ARCH_NATIVE
, SCMP_ACT_ALLOW
);
301 return log_warning_errno(r
, "Failed to acquire seccomp context, ignoring: %m");
303 r
= seccomp_rule_add_exact(
305 SCMP_ACT_ERRNO(EPERM
),
306 SCMP_SYS(close_range
),
309 return log_warning_errno(r
, "Failed to add close_range() rule, ignoring: %m");
311 r
= seccomp_load(seccomp
);
313 return log_warning_errno(r
, "Failed to apply close_range() restrictions, ignoring: %m");
317 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "Seccomp support or close_range() syscall definition not available.");
321 TEST(close_all_fds
) {
324 /* Runs the test four times. Once as is. Once with close_range() syscall blocked via seccomp, once
325 * with /proc/ overmounted, and once with the combination of both. This should trigger all fallbacks
326 * in the close_range_all() function. */
328 r
= safe_fork("(caf-plain)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
, NULL
);
330 test_close_all_fds_inner();
336 return (void) log_tests_skipped("Lacking privileges for test with close_range() blocked and /proc/ overmounted");
338 r
= safe_fork("(caf-noproc)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
|FORK_NEW_MOUNTNS
|FORK_MOUNTNS_SLAVE
, NULL
);
340 r
= mount_nofollow_verbose(LOG_WARNING
, "tmpfs", "/proc", "tmpfs", 0, NULL
);
342 log_notice("Overmounting /proc/ didn't work, skipping close_all_fds() with masked /proc/.");
344 test_close_all_fds_inner();
349 if (!is_seccomp_available())
350 return (void) log_tests_skipped("Seccomp not available");
352 r
= safe_fork("(caf-seccomp)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
, NULL
);
354 r
= seccomp_prohibit_close_range();
356 log_notice("Applying seccomp filter didn't work, skipping close_all_fds() test with masked close_range().");
358 test_close_all_fds_inner();
364 r
= safe_fork("(caf-scnp)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
|FORK_NEW_MOUNTNS
|FORK_MOUNTNS_SLAVE
, NULL
);
366 r
= seccomp_prohibit_close_range();
368 log_notice("Applying seccomp filter didn't work, skipping close_all_fds() test with masked close_range().");
370 r
= mount_nofollow_verbose(LOG_WARNING
, "tmpfs", "/proc", "tmpfs", 0, NULL
);
372 log_notice("Overmounting /proc/ didn't work, skipping close_all_fds() with masked /proc/.");
374 test_close_all_fds_inner();
377 test_close_all_fds_inner();
383 TEST(format_proc_fd_path
) {
384 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(0), "/proc/self/fd/0"));
385 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(1), "/proc/self/fd/1"));
386 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2), "/proc/self/fd/2"));
387 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(3), "/proc/self/fd/3"));
388 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2147483647), "/proc/self/fd/2147483647"));
392 _cleanup_close_
int fd1
= -EBADF
, fd2
= -EBADF
;
393 struct stat st1
, st2
;
396 /* Test this with a directory */
397 fd1
= open("/proc", O_DIRECTORY
|O_PATH
|O_CLOEXEC
);
400 assert_se(fstat(fd1
, &st1
) >= 0);
401 assert_se(S_ISDIR(st1
.st_mode
));
403 fl
= fcntl(fd1
, F_GETFL
);
405 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
406 assert_se(FLAGS_SET(fl
, O_PATH
));
408 fd2
= fd_reopen(fd1
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
); /* drop the O_PATH */
411 assert_se(fstat(fd2
, &st2
) >= 0);
412 assert_se(S_ISDIR(st2
.st_mode
));
413 assert_se(st1
.st_ino
== st2
.st_ino
);
414 assert_se(st1
.st_rdev
== st2
.st_rdev
);
416 fl
= fcntl(fd2
, F_GETFL
);
418 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
419 assert_se(!FLAGS_SET(fl
, O_PATH
));
423 fd1
= fd_reopen(fd2
, O_DIRECTORY
|O_PATH
|O_CLOEXEC
); /* reacquire the O_PATH */
426 assert_se(fstat(fd1
, &st1
) >= 0);
427 assert_se(S_ISDIR(st1
.st_mode
));
428 assert_se(st1
.st_ino
== st2
.st_ino
);
429 assert_se(st1
.st_rdev
== st2
.st_rdev
);
431 fl
= fcntl(fd1
, F_GETFL
);
433 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
434 assert_se(FLAGS_SET(fl
, O_PATH
));
438 /* And now, test this with a file. */
439 fd1
= open("/proc/version", O_PATH
|O_CLOEXEC
);
442 assert_se(fstat(fd1
, &st1
) >= 0);
443 assert_se(S_ISREG(st1
.st_mode
));
445 fl
= fcntl(fd1
, F_GETFL
);
447 assert_se(!FLAGS_SET(fl
, O_DIRECTORY
));
448 assert_se(FLAGS_SET(fl
, O_PATH
));
450 assert_se(fd_reopen(fd1
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
) == -ENOTDIR
);
451 fd2
= fd_reopen(fd1
, O_RDONLY
|O_CLOEXEC
); /* drop the O_PATH */
454 assert_se(fstat(fd2
, &st2
) >= 0);
455 assert_se(S_ISREG(st2
.st_mode
));
456 assert_se(st1
.st_ino
== st2
.st_ino
);
457 assert_se(st1
.st_rdev
== st2
.st_rdev
);
459 fl
= fcntl(fd2
, F_GETFL
);
461 assert_se(!FLAGS_SET(fl
, O_DIRECTORY
));
462 assert_se(!FLAGS_SET(fl
, O_PATH
));
466 assert_se(fd_reopen(fd2
, O_DIRECTORY
|O_PATH
|O_CLOEXEC
) == -ENOTDIR
);
467 fd1
= fd_reopen(fd2
, O_PATH
|O_CLOEXEC
); /* reacquire the O_PATH */
470 assert_se(fstat(fd1
, &st1
) >= 0);
471 assert_se(S_ISREG(st1
.st_mode
));
472 assert_se(st1
.st_ino
== st2
.st_ino
);
473 assert_se(st1
.st_rdev
== st2
.st_rdev
);
475 fl
= fcntl(fd1
, F_GETFL
);
477 assert_se(!FLAGS_SET(fl
, O_DIRECTORY
));
478 assert_se(FLAGS_SET(fl
, O_PATH
));
480 /* Also check the right error is generated if the fd is already closed */
482 assert_se(fd_reopen(fd1
, O_RDONLY
|O_CLOEXEC
) == -EBADF
);
486 TEST(fd_reopen_condition
) {
487 _cleanup_close_
int fd1
= -EBADF
, fd3
= -EBADF
;
490 /* Open without O_PATH */
491 fd1
= open("/usr/", O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
);
494 fl
= fcntl(fd1
, F_GETFL
);
495 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
496 assert_se(!FLAGS_SET(fl
, O_PATH
));
498 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
, O_DIRECTORY
|O_PATH
, &fd3
);
499 assert_se(fd2
== fd1
);
502 /* Switch on O_PATH */
503 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
|O_PATH
, O_DIRECTORY
|O_PATH
, &fd3
);
504 assert_se(fd2
!= fd1
);
505 assert_se(fd3
== fd2
);
507 fl
= fcntl(fd2
, F_GETFL
);
508 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
509 assert_se(FLAGS_SET(fl
, O_PATH
));
511 close_and_replace(fd1
, fd3
);
513 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
|O_PATH
, O_DIRECTORY
|O_PATH
, &fd3
);
514 assert_se(fd2
== fd1
);
517 /* Switch off O_PATH again */
518 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
, O_DIRECTORY
|O_PATH
, &fd3
);
519 assert_se(fd2
!= fd1
);
520 assert_se(fd3
== fd2
);
522 fl
= fcntl(fd2
, F_GETFL
);
523 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
524 assert_se(!FLAGS_SET(fl
, O_PATH
));
526 close_and_replace(fd1
, fd3
);
528 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
, O_DIRECTORY
|O_PATH
, &fd3
);
529 assert_se(fd2
== fd1
);
534 _cleanup_close_
int fd1
= -EBADF
, fd2
= -EBADF
;
535 int array
[2] = { -EBADF
, -EBADF
}, i
= 0;
537 assert_se(fd1
== -EBADF
);
538 assert_se(fd2
== -EBADF
);
540 fd1
= eventfd(0, EFD_CLOEXEC
);
544 assert_se(fd1
== -EBADF
);
547 assert_se(array
[0] == -EBADF
);
548 assert_se(array
[1] == -EBADF
);
550 array
[0] = TAKE_FD(fd2
);
551 assert_se(fd1
== -EBADF
);
552 assert_se(fd2
== -EBADF
);
553 assert_se(array
[0] >= 0);
554 assert_se(array
[1] == -EBADF
);
556 array
[1] = TAKE_FD(array
[i
]);
557 assert_se(array
[0] == -EBADF
);
558 assert_se(array
[1] >= 0);
561 array
[0] = TAKE_FD(*(array
+ i
));
562 assert_se(array
[0] >= 0);
563 assert_se(array
[1] == -EBADF
);
566 fd1
= TAKE_FD(array
[i
]);
568 assert_se(array
[0] == -EBADF
);
569 assert_se(array
[1] == -EBADF
);
572 DEFINE_TEST_MAIN(LOG_DEBUG
);