sys-socket: Add spice_socketpair helper that makes sockets FD_CLOEXEC

Non-CLOEXEC descriptors leak on QEMU cpr-exec migration.
QEMU has all of its internal sockets CLOEXEC'd,
and expects the external libraries to do the same.

Signed-off-by: Polina Vishneva <polina.vishneva@virtuozzo.com>
Acked-by: Frediano Ziglio <freddy77@gmail.com>
This commit is contained in:
Polina Vishneva 2026-01-28 21:31:38 +01:00 committed by Frediano Ziglio
parent bf1be3d08e
commit e45861f995
9 changed files with 58 additions and 17 deletions

View File

@ -98,5 +98,6 @@ Patches also contributed by
Daniel Kahn Gillmor <dkg@fifthhorseman.net>
Nicholas Vinson <nvinson234@gmail.com>
yanlonglong <yanlonglong@kylinos.cn>
Polina Vishneva <polina.vishneva@virtuozzo.com>
....send patches to get your name here...

View File

@ -85,7 +85,7 @@ Dispatcher::Dispatcher(uint32_t max_message_type):
{
int channels[2];
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, channels) == -1) {
if (socket_newpair(SOCK_STREAM, 0, channels) == -1) {
spice_error("socketpair failed %s", strerror(errno));
return;
}

View File

@ -34,7 +34,40 @@
#include "sys-socket.h"
#ifdef _WIN32
#ifndef _WIN32
int socket_newpair(int type, int protocol, int sv[2])
{
const int domain = AF_LOCAL;
int ret;
int flags;
#ifdef SOCK_CLOEXEC
ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv);
if (!(ret == -1 && errno == EINVAL)) {
/* Failure isn't caused by the lack of SOCK_CLOEXEC support
* in the currently running kernel */
return ret;
}
#endif
ret = socketpair(domain, type, protocol, sv);
if (ret == -1) {
return ret;
}
flags = fcntl(sv[0], F_GETFD);
if (flags != -1) {
fcntl(sv[0], F_SETFD, flags | FD_CLOEXEC);
}
flags = fcntl(sv[1], F_GETFD);
if (flags != -1) {
fcntl(sv[1], F_SETFD, flags | FD_CLOEXEC);
}
return ret;
}
#else
// Map Windows socket errors to standard C ones
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
void socket_win32_set_errno(void)
@ -208,6 +241,11 @@ SPICE_CONSTRUCTOR_FUNC(socket_win32_init)
WSAStartup(MAKEWORD(2, 2), &wsaData);
}
static inline SOCKET socket_inet_noinherit(int type)
{
return WSASocketW(AF_INET, type, 0, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT);
}
int socket_newpair(int type, int protocol, int sv[2])
{
struct sockaddr_in sa, sa2;
@ -219,14 +257,14 @@ int socket_newpair(int type, int protocol, int sv[2])
}
/* create a listener */
s = socket(AF_INET, type, 0);
s = socket_inet_noinherit(type);
if (s == INVALID_SOCKET) {
return -1;
}
pairs[1] = INVALID_SOCKET;
pairs[0] = socket(AF_INET, type, 0);
pairs[0] = socket_inet_noinherit(type);
if (pairs[0] == INVALID_SOCKET) {
goto cleanup;
}

View File

@ -21,6 +21,10 @@
#ifndef RED_SYS_SOCKET_H_
#define RED_SYS_SOCKET_H_
#include <spice/macros.h>
SPICE_BEGIN_DECLS
#ifndef _WIN32
# include <sys/socket.h>
@ -32,9 +36,6 @@
#else
# include <winsock2.h>
# include <windows.h>
# include <spice/macros.h>
SPICE_BEGIN_DECLS
typedef int socklen_t;
@ -139,15 +140,15 @@ socket_accept(int sock, struct sockaddr *addr, int *addrlen)
#undef accept
#define accept socket_accept
int socket_newpair(int type, int protocol, int sv[2]);
#define socketpair(family, type, protocol, sv) socket_newpair(type, protocol, sv)
SPICE_END_DECLS
#endif
#if defined(SO_NOSIGPIPE) && defined(__APPLE__)
#define MSG_NOSIGNAL 0
#endif
// Ensures FD_CLOEXEC flag is set if available, in the best way available
int socket_newpair(int type, int protocol, int sv[2]);
SPICE_END_DECLS
#endif // RED_SYS_SOCKET_H_

View File

@ -183,7 +183,7 @@ static void timeout_watch_count(void *opaque)
static RedStream *create_dummy_stream(SpiceServer *server, int *p_socket)
{
int sv[2];
g_assert_cmpint(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv), ==, 0);
g_assert_cmpint(socket_newpair(SOCK_STREAM, 0, sv), ==, 0);
if (p_socket) {
*p_socket = sv[1];
}

View File

@ -77,7 +77,7 @@ static void server_leaks(void)
#endif
g_test_expect_message(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
"*SSL_accept failed*");
g_assert_cmpint(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv), ==, 0);
g_assert_cmpint(socket_newpair(SOCK_STREAM, 0, sv), ==, 0);
socket_close(sv[1]);
result = spice_server_add_ssl_client(server, sv[0], 1);
g_assert_cmpint(result, ==, -1);

View File

@ -32,6 +32,7 @@
#include "test-glib-compat.h"
#include "basic-event-loop.h"
#include "sys-socket.h"
#include <spice/start-packed.h>
typedef struct SPICE_ATTR_PACKED SpiceInitialMessage {
@ -602,7 +603,7 @@ setup_thread(void)
test_num, len, data->mechname, len, data->line);
int sv[2];
g_assert_cmpint(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv), ==, 0);
g_assert_cmpint(socket_newpair(SOCK_STREAM, 0, sv), ==, 0);
g_assert(spice_server_add_client(server, sv[0], 0) == 0);

View File

@ -82,7 +82,7 @@ static void test_smartcard_teardown(TestFixture *fixture, gconstpointer user_dat
static RedStream *create_dummy_stream(SpiceServer *server, int *p_socket)
{
int sv[2];
g_assert_cmpint(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv), ==, 0);
g_assert_cmpint(socket_newpair(SOCK_STREAM, 0, sv), ==, 0);
if (p_socket) {
*p_socket = sv[1];
}

View File

@ -106,7 +106,7 @@ int main(int argc, char *argv[])
spice_return_val_if_fail(server_init() == 0, -1);
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) == -1) {
if (socket_newpair(SOCK_STREAM, 0, sv) == -1) {
spice_error("socketpair failed %s", strerror(errno));
return -1;
}