diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index dec083daaf..274bbb32cc 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -208,6 +208,41 @@ static int get_max_fd(void) { return (int) (m - 1); } +int close_all_fds_without_malloc(const int except[], size_t n_except) { + int max_fd, r = 0; + + assert(n_except == 0 || except); + + /* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so + * and hence is safe to be called in signal handler context. Most users should call close_all_fds(), + * but when we assume we are called from signal handler context, then use this simpler call + * instead. */ + + max_fd = get_max_fd(); + if (max_fd < 0) + return max_fd; + + /* Refuse to do the loop over more too many elements. It's better to fail immediately than to + * spin the CPU for a long time. */ + if (max_fd > MAX_FD_LOOP_LIMIT) + return log_debug_errno(SYNTHETIC_ERRNO(EPERM), + "Refusing to loop over %d potential fds.", + max_fd); + + for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { + int q; + + if (fd_in_set(fd, except, n_except)) + continue; + + q = close_nointr(fd); + if (q < 0 && q != -EBADF && r >= 0) + r = q; + } + + return r; +} + int close_all_fds(const int except[], size_t n_except) { static bool have_close_range = true; /* Assume we live in the future */ _cleanup_closedir_ DIR *d = NULL; @@ -301,36 +336,8 @@ int close_all_fds(const int except[], size_t n_except) { } d = opendir("/proc/self/fd"); - if (!d) { - int fd, max_fd; - - /* When /proc isn't available (for example in chroots) the fallback is brute forcing through - * the fd table */ - - max_fd = get_max_fd(); - if (max_fd < 0) - return max_fd; - - /* Refuse to do the loop over more too many elements. It's better to fail immediately than to - * spin the CPU for a long time. */ - if (max_fd > MAX_FD_LOOP_LIMIT) - return log_debug_errno(SYNTHETIC_ERRNO(EPERM), - "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.", - max_fd); - - for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { - int q; - - if (fd_in_set(fd, except, n_except)) - continue; - - q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) - r = q; - } - - return r; - } + if (!d) + return close_all_fds_without_malloc(except, n_except); /* ultimate fallback if /proc/ is not available */ FOREACH_DIRENT(de, d, return -errno) { int fd = -1, q; diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 459059c64e..e929386b53 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -58,6 +58,7 @@ int fd_nonblock(int fd, bool nonblock); int fd_cloexec(int fd, bool cloexec); int close_all_fds(const int except[], size_t n_except); +int close_all_fds_without_malloc(const int except[], size_t n_except); int same_fd(int a, int b);