Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed close handles with Alpine and MUSL #376

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 65 additions & 11 deletions include/boost/process/v2/posix/detail/close_handles.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/posix/detail/close_handles.hpp>
// linux has close_range since 5.19
// linux has close_range since 5.9
// see: https://man.archlinux.org/man/close_range.2.en#HISTORY
// https://elixir.bootlin.com/linux/v5.9/source/fs/open.c#L1318


#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
Expand All @@ -31,7 +33,7 @@
#include <unistd.h>
#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM 1

#elif defined(__sun)
#elif defined(__sun) // if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)

/*https://docs.oracle.com/cd/E36784_01/html/E36874/closefrom-3c.html

Expand All @@ -41,22 +43,44 @@ int fdwalk(int (*func)(void *, int), void *cd);
#include <stdlib.h>
#define BOOST_PROCESS_V2_HAS_PDFORK 1

#elif defined(__linux__)
#elif defined(__linux__) // elif defined(__sun)

#include <linux/version.h>
// define BOOST_PROCESS_V2_POSIX_FORCE_DISABLE_CLOSE_RANGE to force disable any usage of ::close_range()


#include <sys/syscall.h>

// All checks here are intended to check whether target system has ::close_range()
// Note that this checks only valid on glibc. MUSL doens't have ::close_range() anyway. So we don't care about any
// MUSL library macroses
#if defined(__GLIBC__)

// On glibc we also must have ::close_range() wrapper around system call.
// If we're building on system with new kernel, BUT without this wrapper (old glibc) the build will be failed
#include <gnu/libc-version.h>
#if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 34) && !defined(BOOST_PROCESS_V2_POSIX_FORCE_DISABLE_CLOSE_RANGE)
#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1
#endif // __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 34

#endif // ifdef __GLIBC__

// glibc version doesn't meet version requirements or we're building with MUSL.
// Just try to check linux version code. If system call is supported we'll use raw system call
#if !defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE) && defined(SYS_close_range) && !defined(BOOST_PROCESS_V2_POSIX_FORCE_DISABLE_CLOSE_RANGE)
#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE_SYSCALL 1
#endif // if !defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE) && LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)

// https://man7.org/linux/man-pages/man2/close_range.2.html
#if defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE) || defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_SYSCALL)
#include <linux/close_range.h>
#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1
#else
#include <sys/syscall.h>

#else // ifdef BOOST_PROCESS_V2_HAS_CLOSE_RANGE || defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_SYSCALL)
#include <dirent.h>
#endif // BOOST_PROCESS_V2_HAS_CLOSE_RANGE || defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_SYSCALL)

#endif

#else
#else // elif defined(__linux__)

#include <dirent.h>

Expand Down Expand Up @@ -145,6 +169,36 @@ void close_all(const std::vector<int> & whitelist, error_code & ec)
::close_range(0, std::numeric_limits<int>::max(), CLOSE_RANGE_UNSHARE);
}

#elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_SYSCALL)

// linux impl - whitelist must be ordered
void close_all(const std::vector<int> & whitelist, error_code & ec)
{
// https://patchwork.kernel.org/project/linux-fsdevel/cover/[email protected]/
//the most common scenario is whitelist = {0,1,2}
if (!whitelist.empty())
{
if (whitelist.front() != 0)
::syscall(SYS_close_range, 0, whitelist.front() - 1, CLOSE_RANGE_UNSHARE);

for (std::size_t idx = 0u;
idx < (whitelist.size() - 1u);
idx++)
{
const auto mine = whitelist[idx];
const auto next = whitelist[idx + 1];
if ((mine + 1) != next && (mine != next))
{
::syscall(SYS_close_range, mine + 1, next - 1, CLOSE_RANGE_UNSHARE);
}
}

::syscall(SYS_close_range, whitelist.back() + 1, std::numeric_limits<int>::max(), CLOSE_RANGE_UNSHARE);
}
else
::syscall(SYS_close_range, 0, std::numeric_limits<int>::max(), CLOSE_RANGE_UNSHARE);
}

#else

// default one
Expand Down Expand Up @@ -191,4 +245,4 @@ void close_all(const std::vector<int> & whitelist, error_code & ec)

BOOST_PROCESS_V2_END_NAMESPACE

#endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP
#endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP