block/curl.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-)
From 5cf4935ab0770d2d45d32ae5bb3531c0c9a5848e Mon Sep 17 00:00:00 2001
From: Sheldon Qi <sheldon.qi@canonical.com>
Date: Thu, 28 May 2026 21:28:06 +0800
Subject: [PATCH] block/curl: fix Win32 socket handling in curl_sock_cb
On Windows libcurl passes raw SOCKET values (UINT_PTR, 64-bit on x64)
that don't fit in int, so GINT_TO_POINTER() truncates the upper bits.
Wrap them in CRT fds via _open_osfhandle / _get_osfhandle so that
aio_set_fd_handler and curl_multi_socket_action receive valid CRT fds.
This matches the pattern already used by io/channel-watch.c,
ui/spice-core.c, monitor/fds.c, and util/aio-win32.c.
Close the CRT fd wrapper in curl_drop_socket and on CURL_POLL_REMOVE
so libcurl retains ownership of the underlying SOCKET.
Signed-off-by: Sheldon Qi <sheldon.qi@canonical.com>
---
block/curl.c | 44 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 41 insertions(+), 3 deletions(-)
diff --git a/block/curl.c b/block/curl.c
index 684c677ef7..0884d96fe6 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -110,7 +110,8 @@ typedef struct BDRVCURLState {
QEMUTimer timer;
uint64_t len;
CURLState states[CURL_NUM_STATES];
- GHashTable *sockets; /* GINT_TO_POINTER(fd) -> socket */
+ /* GSIZE_TO_POINTER(raw_sock) on Win32, GINT_TO_POINTER(fd) elsewhere. */
+ GHashTable *sockets;
char *url;
size_t readahead_size;
bool sslverify;
@@ -136,6 +137,10 @@ static gboolean curl_drop_socket(void *key, void
*value, void *opaque)
aio_set_fd_handler(s->aio_context, socket->fd,
NULL, NULL, NULL, NULL, NULL);
+#ifdef _WIN32
+ /* Close CRT fd wrapper; libcurl owns the underlying SOCKET. */
+ qemu_close_socket_osfhandle(socket->fd);
+#endif
return true;
}
@@ -167,13 +172,29 @@ static int curl_sock_cb(CURL *curl,
curl_socket_t fd, int action,
BDRVCURLState *s = userp;
CURLSocket *socket;
+#ifdef _WIN32
+ /*
+ * libcurl passes raw SOCKET (UINT_PTR, 64-bit on x64) which doesn't
+ * fit in GINT_TO_POINTER; wrap in a CRT fd and use GSIZE_TO_POINTER
+ * for the hash key.
+ */
+ socket = g_hash_table_lookup(s->sockets, GSIZE_TO_POINTER((gsize)fd));
+#else
socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd));
+#endif
if (!socket) {
socket = g_new0(CURLSocket, 1);
- socket->fd = fd;
socket->s = s;
+#ifdef _WIN32
+ socket->fd = _open_osfhandle(fd, _O_BINARY);
+ g_hash_table_insert(s->sockets, GSIZE_TO_POINTER((gsize)fd), socket);
+#else
+ socket->fd = fd;
g_hash_table_insert(s->sockets, GINT_TO_POINTER(fd), socket);
+#endif
}
+ /* Switch to the CRT fd so the rest of the function stays unchanged. */
+ fd = (curl_socket_t)(intptr_t)socket->fd;
trace_curl_sock_cb(action, (int)fd);
switch (action) {
@@ -197,7 +218,17 @@ static int curl_sock_cb(CURL *curl, curl_socket_t
fd, int action,
}
if (action == CURL_POLL_REMOVE) {
+#ifdef _WIN32
+ {
+ /* _get_osfhandle recovers the raw SOCKET for the hash key. */
+ curl_socket_t raw_sock = _get_osfhandle(socket->fd);
+ qemu_close_socket_osfhandle(socket->fd);
+ g_hash_table_remove(s->sockets,
+ GSIZE_TO_POINTER((gsize)raw_sock));
+ }
+#else
g_hash_table_remove(s->sockets, GINT_TO_POINTER(fd));
+#endif
}
return 0;
@@ -409,13 +440,20 @@ static void curl_multi_do_locked(CURLSocket *socket)
BDRVCURLState *s = socket->s;
int running;
int r;
+ curl_socket_t sockfd;
+#ifdef _WIN32
+ /* socket->fd is a CRT fd; _get_osfhandle gets the raw SOCKET. */
+ sockfd = _get_osfhandle(socket->fd);
+#else
+ sockfd = socket->fd;
+#endif
if (!s->multi) {
return;
}
do {
- r = curl_multi_socket_action(s->multi, socket->fd, 0, &running);
+ r = curl_multi_socket_action(s->multi, sockfd, 0, &running);
} while (r == CURLM_CALL_MULTI_PERFORM);
}
--
2.47.3
© 2016 - 2026 Red Hat, Inc.