From f4afed90e605074e86900da9e9ee065cec91aa5b Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 5 Feb 2024 20:29:47 +0000 Subject: [PATCH] tests: Exercise g_fdwalk_set_cloexec() and g_closefrom() Signed-off-by: Simon McVittie --- glib/tests/unix.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/glib/tests/unix.c b/glib/tests/unix.c index e4003f730..8f624c527 100644 --- a/glib/tests/unix.c +++ b/glib/tests/unix.c @@ -36,6 +36,187 @@ #include "testutils.h" +static void +async_signal_safe_message (const char *message) +{ + if (write (2, message, strlen (message)) < 0 || + write (2, "\n", 1) < 0) + { + /* ignore: not much we can do */ + } +} + +static void +test_closefrom (void) +{ + /* Enough file descriptors to be confident that we're operating on + * all of them */ + const int N_FDS = 20; + int *fds; + int fd; + int i; + pid_t child; + int wait_status; + + /* The loop that populates @fds with pipes assumes this */ + g_assert (N_FDS % 2 == 0); + + g_test_summary ("Test g_closefrom(), g_fdwalk_set_cloexec()"); + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3247"); + + for (fd = 0; fd <= 2; fd++) + { + int flags; + + g_assert_no_errno ((flags = fcntl (fd, F_GETFD))); + g_assert_no_errno (fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC)); + } + + fds = g_new0 (int, N_FDS); + + for (i = 0; i < N_FDS; i += 2) + { + GError *error = NULL; + int pipefd[2]; + int res; + + /* Intentionally neither O_CLOEXEC nor FD_CLOEXEC */ + res = g_unix_open_pipe (pipefd, 0, &error); + g_assert (res); + g_assert_no_error (error); + g_clear_error (&error); + fds[i] = pipefd[0]; + fds[i + 1] = pipefd[1]; + } + + child = fork (); + + /* Child process exits with status = 100 + the first wrong fd, + * or 0 if all were correct */ + if (child == 0) + { + for (i = 0; i < N_FDS; i++) + { + int flags = fcntl (fds[i], F_GETFD); + + if (flags == -1) + { + async_signal_safe_message ("fd should not have been closed"); + _exit (100 + fds[i]); + } + + if (flags & FD_CLOEXEC) + { + async_signal_safe_message ("fd should not have been close-on-exec yet"); + _exit (100 + fds[i]); + } + } + + g_fdwalk_set_cloexec (3); + + for (i = 0; i < N_FDS; i++) + { + int flags = fcntl (fds[i], F_GETFD); + + if (flags == -1) + { + async_signal_safe_message ("fd should not have been closed"); + _exit (100 + fds[i]); + } + + if (!(flags & FD_CLOEXEC)) + { + async_signal_safe_message ("fd should have been close-on-exec"); + _exit (100 + fds[i]); + } + } + + g_closefrom (3); + + for (fd = 0; fd <= 2; fd++) + { + int flags = fcntl (fd, F_GETFD); + + if (flags == -1) + { + async_signal_safe_message ("fd should not have been closed"); + _exit (100 + fd); + } + + if (flags & FD_CLOEXEC) + { + async_signal_safe_message ("fd should not have been close-on-exec"); + _exit (100 + fd); + } + } + + for (i = 0; i < N_FDS; i++) + { + if (fcntl (fds[i], F_GETFD) != -1 || errno != EBADF) + { + async_signal_safe_message ("fd should have been closed"); + _exit (100 + fds[i]); + } + } + + _exit (0); + } + + g_assert_no_errno (waitpid (child, &wait_status, 0)); + + if (WIFEXITED (wait_status)) + { + int exit_status = WEXITSTATUS (wait_status); + + if (exit_status != 0) + g_test_fail_printf ("File descriptor %d in incorrect state", exit_status - 100); + } + else + { + g_test_fail_printf ("Unexpected wait status %d", wait_status); + } + + for (i = 0; i < N_FDS; i++) + { + GError *error = NULL; + + g_close (fds[i], &error); + g_assert_no_error (error); + g_clear_error (&error); + } + + g_free (fds); + + if (g_test_undefined ()) + { + g_test_trap_subprocess ("/glib-unix/closefrom/subprocess/einval", + 0, G_TEST_SUBPROCESS_DEFAULT); + g_test_trap_assert_passed (); + } +} + +static void +test_closefrom_subprocess_einval (void) +{ + int res; + int errsv; + + g_log_set_always_fatal (G_LOG_FATAL_MASK); + g_log_set_fatal_mask ("GLib", G_LOG_FATAL_MASK); + + errno = 0; + res = g_closefrom (-1); + errsv = errno; + g_assert_cmpint (res, ==, -1); + g_assert_cmpint (errsv, ==, EINVAL); + + errno = 0; + res = g_fdwalk_set_cloexec (-42); + errsv = errno; + g_assert_cmpint (res, ==, -1); + g_assert_cmpint (errsv, ==, EINVAL); +} + static void test_pipe (void) { @@ -613,6 +794,9 @@ main (int argc, { g_test_init (&argc, &argv, NULL); + g_test_add_func ("/glib-unix/closefrom", test_closefrom); + g_test_add_func ("/glib-unix/closefrom/subprocess/einval", + test_closefrom_subprocess_einval); g_test_add_func ("/glib-unix/pipe", test_pipe); g_test_add_func ("/glib-unix/pipe/fd-cloexec", test_pipe_fd_cloexec); g_test_add_func ("/glib-unix/pipe-stdio-overwrite", test_pipe_stdio_overwrite);