From 4b37b73d888c1a3f58465b7b89d6e1ed7599fe11 Mon Sep 17 00:00:00 2001 From: Fabien Romano Date: Wed, 21 Oct 2026 22:54:30 +0100 Subject: [PATCH] Fix file descriptor cleanup on OpenBSD OpenBSD's /dev/fd contains static device nodes rather than a list of open descriptors. Enumerating it can encounter closed descriptors and miss open descriptors beyond the available nodes. In Process v2, use close() between retained descriptors and closefrom() above the last one, avoiding /dev/fd enumeration. For Process v1, deliberately keep the fix minimal: ignore EBADF and retry close() on EINTR, rather than rewriting the cleanup algorithm to follow the v2 approach. This prevents limit_handles failures but leaves descriptors missing from /dev/fd unhandled. --- .../boost/process/v1/detail/posix/handles.hpp | 3 +- src/posix/close_handles.cpp | 46 ++++++++++++++++--- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/include/boost/process/v1/detail/posix/handles.hpp b/include/boost/process/v1/detail/posix/handles.hpp index bd1b498e4..806a6399e 100644 --- a/include/boost/process/v1/detail/posix/handles.hpp +++ b/include/boost/process/v1/detail/posix/handles.hpp @@ -133,8 +133,9 @@ struct limit_handles_ : handler_base_ext if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end()) continue; - if (::close(conv) != 0) + if (::close(conv) == -1 && errno != EBADF) { + while (errno == EINTR && close(conv) == -1); exec.set_error(::boost::process::v1::detail::get_last_error(), "close() failed"); return; } diff --git a/src/posix/close_handles.cpp b/src/posix/close_handles.cpp index 5795a3b1c..961f24f60 100644 --- a/src/posix/close_handles.cpp +++ b/src/posix/close_handles.cpp @@ -24,12 +24,20 @@ // // gives us // -// int closefrom(int fd); +// void closefrom(int lowfd); // int close_range(u_int lowfd, u_int highfd, int flags); #include #define BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM 1 +#elif defined(__OpenBSD__) + +// https://man.openbsd.org/closefrom.2 +// int closefrom(int fd); + +#include +#define BOOST_PROCESS_V2_HAS_CLOSEFROM 1 + #elif defined(__sun) /*https://docs.oracle.com/cd/E36784_01/html/E36874/closefrom-3c.html @@ -113,16 +121,25 @@ void close_all(const std::vector & whitelist, error_code & ec) ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); } -#elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM) +#elif defined(BOOST_PROCESS_V2_HAS_CLOSEFROM) || \ + defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM) -// freeBSD impl - whitelist must be ordered -void close_all(const std::vector & whitelist, error_code & ec) +/* FreeBSD and OpenBSD implementation - whitelist must be ordered. + * closefrom() differ, handle both implementation : reset errno and check EINTR. + */ +void close_all(const std::vector & whitelist, error_code & /*ec*/) { //the most common scenario is whitelist = {0,1,2} if (!whitelist.empty()) { - if (whitelist.front() != 0) + if (whitelist.front() != 0) { +#if defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM) ::close_range(0, whitelist.front() - 1, 0); +#else + for (int fd = 0; fd < whitelist.front(); fd++) + while (::close(fd) == -1 && errno == EINTR); +#endif + } for (std::size_t idx = 0u; idx < (whitelist.size() - 1u); @@ -132,14 +149,29 @@ void close_all(const std::vector & whitelist, error_code & ec) const auto next = whitelist[idx + 1]; if ((mine + 1) != next && (mine != next)) { +#if defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM) ::close_range(mine + 1, next - 1, 0); +#else + for (int fd = mine + 1; fd < next; fd++) + while (::close(fd) == -1 && errno == EINTR); +#endif } } - ::closefrom(whitelist.back() + 1); + do + { + errno = 0; + ::closefrom(whitelist.back() + 1); + } while (errno == EINTR); } else - ::closefrom(0); + { + do + { + errno = 0; + ::closefrom(0); + } while (errno == EINTR); + } } #elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE)