From 1fe1d5d541fd54ce6d49b59b6a8dcf1079d2be49 Mon Sep 17 00:00:00 2001 From: edef Date: Thu, 16 Mar 2023 09:43:58 +0000 Subject: [PATCH] common: use close_range on Linux Fix rook/rook#10110, which occurs when _SC_OPEN_MAX/RLIMIT_NOFILE is set to very large values (2^30), leaving fork_function pegging a core busylooping. The glibc wrappers closefrom(3)/close_range(3) are not available before glibc 2.34, so we invoke the syscall directly. When glibc 2.34 is old enough to be a reasonable hard minimum dependency, we should switch to using closefrom. If we're not running on (recent enough) Linux, we fall back to the existing approach. Fixes: https://tracker.ceph.com/issues/59125 Signed-off-by: edef (cherry picked from commit 1a33cefa1980a18b11479275c1f708c1de24ee37) --- src/common/SubProcess.cc | 9 +++++++++ src/common/fork_function.h | 31 ++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/common/SubProcess.cc b/src/common/SubProcess.cc index 1faf33e36eeeb..8f28ff25ca215 100644 --- a/src/common/SubProcess.cc +++ b/src/common/SubProcess.cc @@ -4,6 +4,9 @@ #include #include #endif +#ifdef __linux__ +#include +#endif #include #include #include @@ -200,6 +203,12 @@ int SubProcess::spawn() { int maxfd = sysconf(_SC_OPEN_MAX); if (maxfd == -1) maxfd = 16384; + +#if defined(__linux__) && defined(SYS_close_range) + if (::syscall(SYS_close_range, STDERR_FILENO + 1, ~0U, 0) == 0) + maxfd = STDERR_FILENO; +#endif + for (int fd = 0; fd <= maxfd; fd++) { if (fd == STDIN_FILENO && stdin_op != CLOSE) continue; diff --git a/src/common/fork_function.h b/src/common/fork_function.h index 3a4f2f29c08a2..5c94be4dcaf72 100644 --- a/src/common/fork_function.h +++ b/src/common/fork_function.h @@ -13,6 +13,9 @@ #ifndef _WIN32 #include #endif +#ifdef __linux__ +#include +#endif #include #include "include/ceph_assert.h" @@ -53,17 +56,23 @@ static inline int fork_function( // we are forker (first child) // close all fds - int maxfd = sysconf(_SC_OPEN_MAX); - if (maxfd == -1) - maxfd = 16384; - for (int fd = 0; fd <= maxfd; fd++) { - if (fd == STDIN_FILENO) - continue; - if (fd == STDOUT_FILENO) - continue; - if (fd == STDERR_FILENO) - continue; - ::close(fd); +#if defined(__linux__) && defined(SYS_close_range) + if (::syscall(SYS_close_range, STDERR_FILENO + 1, ~0U, 0)) +#endif + { + // fall back to manually closing + int maxfd = sysconf(_SC_OPEN_MAX); + if (maxfd == -1) + maxfd = 16384; + for (int fd = 0; fd <= maxfd; fd++) { + if (fd == STDIN_FILENO) + continue; + if (fd == STDOUT_FILENO) + continue; + if (fd == STDERR_FILENO) + continue; + ::close(fd); + } } sigset_t mask, oldmask; -- 2.39.5