From cc7dcd3d18ad5afdbc0831b7c7280f939574b1d9 Mon Sep 17 00:00:00 2001 From: jrtxio Date: Fri, 2 Oct 2026 07:45:57 +0800 Subject: [PATCH 1/2] linux runtime: build on hosts without SOCK_CLOEXEC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SOCK_CLOEXEC is a Linux-only socketpair flag; macOS (the dev-iteration harness for Linux hosts) lacks it, so platform/linux/runtime did not compile outside Linux. Fall back to setting FD_CLOEXEC on both ends right after the pair is created — this thread performs no I/O in between, so no descriptor can leak across an exec in that window. Verified: the BrainFuel GTK4 host now compiles, links, and runs against this runtime on macOS (arm64, libracketcs) with the same sources that CI compiles on Linux. --- platform/linux/runtime/backend.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/platform/linux/runtime/backend.cpp b/platform/linux/runtime/backend.cpp index 4751e7e..48b4440 100644 --- a/platform/linux/runtime/backend.cpp +++ b/platform/linux/runtime/backend.cpp @@ -67,7 +67,20 @@ struct SocketEndpoints { SocketEndpoints create_socket_endpoints() { int fds[2]{-1, -1}; - if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds) != 0) { +#if defined(SOCK_CLOEXEC) + int const rc = ::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); +#else + // Platforms without SOCK_CLOEXEC (macOS): set the flag on both ends right + // after the pair exists; this thread does no I/O in between. + int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + if (rc == 0) { + if (::fcntl(fds[0], F_SETFD, FD_CLOEXEC) != 0 || + ::fcntl(fds[1], F_SETFD, FD_CLOEXEC) != 0) { + rc = -1; + } + } +#endif + if (rc != 0) { throw std::runtime_error("socketpair failed: " + std::string(std::strerror(errno))); } From f48e4e4c26182a79dbe6bea045146980d85ecb46 Mon Sep 17 00:00:00 2001 From: "ren.ji" Date: Thu, 8 Oct 2026 09:22:50 +0800 Subject: [PATCH 2/2] Close fallback socket descriptors on setup failure --- platform/linux/runtime/backend.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/platform/linux/runtime/backend.cpp b/platform/linux/runtime/backend.cpp index 48b4440..76a660c 100644 --- a/platform/linux/runtime/backend.cpp +++ b/platform/linux/runtime/backend.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -70,15 +71,7 @@ SocketEndpoints create_socket_endpoints() { #if defined(SOCK_CLOEXEC) int const rc = ::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); #else - // Platforms without SOCK_CLOEXEC (macOS): set the flag on both ends right - // after the pair exists; this thread does no I/O in between. - int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, fds); - if (rc == 0) { - if (::fcntl(fds[0], F_SETFD, FD_CLOEXEC) != 0 || - ::fcntl(fds[1], F_SETFD, FD_CLOEXEC) != 0) { - rc = -1; - } - } + int const rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, fds); #endif if (rc != 0) { throw std::runtime_error("socketpair failed: " + @@ -87,6 +80,18 @@ SocketEndpoints create_socket_endpoints() { UniqueFd native(fds[0]); UniqueFd server_read(fds[1]); +#if !defined(SOCK_CLOEXEC) + // Platforms without SOCK_CLOEXEC (macOS): set the flag on both ends right + // after the pair exists. Own the descriptors first so an fcntl failure + // cannot leak either endpoint while the exception unwinds. + for (int const fd : {native.get(), server_read.get()}) { + int const flags = ::fcntl(fd, F_GETFD, 0); + if (flags < 0 || ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != 0) { + throw std::runtime_error("fcntl(FD_CLOEXEC) failed: " + + std::string(std::strerror(errno))); + } + } +#endif UniqueFd server_write(::dup(server_read.get())); if (server_write.get() < 0) { throw std::runtime_error("dup failed: " +