[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL v2 13/25] win32: avoid mixing SOCKET and file descriptor space
From: |
marcandre . lureau |
Subject: |
[PULL v2 13/25] win32: avoid mixing SOCKET and file descriptor space |
Date: |
Mon, 13 Mar 2023 15:46:36 +0400 |
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Until now, a win32 SOCKET handle is often cast to an int file
descriptor, as this is what other OS use for sockets. When necessary,
QEMU eventually queries whether it's a socket with the help of
fd_is_socket(). However, there is no guarantee of conflict between the
fd and SOCKET space. Such conflict would have surprising consequences,
we shouldn't mix them.
Also, it is often forgotten that SOCKET must be closed with
closesocket(), and not close().
Instead, let's make the win32 socket wrapper functions return and take a
file descriptor, and let util/ wrappers do the fd/SOCKET conversion as
necessary. A bit of adaptation is necessary in io/ as well.
Unfortunately, we can't drop closesocket() usage, despite
_open_osfhandle() documentation claiming transfer of ownership, testing
shows bad behaviour if you forget to call closesocket().
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Message-Id: <20230221124802.4103554-15-marcandre.lureau@redhat.com>
---
include/sysemu/os-win32.h | 4 +-
io/channel-watch.c | 6 +-
util/aio-win32.c | 9 +-
util/oslib-win32.c | 219 ++++++++++++++++++++++++++++++++------
4 files changed, 197 insertions(+), 41 deletions(-)
diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h
index 504a8966c3..cb1dcce618 100644
--- a/include/sysemu/os-win32.h
+++ b/include/sysemu/os-win32.h
@@ -166,10 +166,10 @@ static inline void qemu_funlockfile(FILE *f)
}
/* Helper for WSAEventSelect, to report errors */
-bool qemu_socket_select(SOCKET s, WSAEVENT hEventObject,
+bool qemu_socket_select(int sockfd, WSAEVENT hEventObject,
long lNetworkEvents, Error **errp);
-bool qemu_socket_unselect(SOCKET s, Error **errp);
+bool qemu_socket_unselect(int sockfd, Error **errp);
/* We wrap all the sockets functions so that we can
* set errno based on WSAGetLastError()
diff --git a/io/channel-watch.c b/io/channel-watch.c
index 6ac41009fa..64b486e378 100644
--- a/io/channel-watch.c
+++ b/io/channel-watch.c
@@ -275,13 +275,13 @@ GSource *qio_channel_create_fd_watch(QIOChannel *ioc,
#ifdef CONFIG_WIN32
GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
- int socket,
+ int sockfd,
GIOCondition condition)
{
GSource *source;
QIOChannelSocketSource *ssource;
- qemu_socket_select(socket, ioc->event,
+ qemu_socket_select(sockfd, ioc->event,
FD_READ | FD_ACCEPT | FD_CLOSE |
FD_CONNECT | FD_WRITE | FD_OOB, NULL);
@@ -293,7 +293,7 @@ GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
object_ref(OBJECT(ioc));
ssource->condition = condition;
- ssource->socket = socket;
+ ssource->socket = _get_osfhandle(sockfd);
ssource->revents = 0;
ssource->fd.fd = (gintptr)ioc->event;
diff --git a/util/aio-win32.c b/util/aio-win32.c
index 08e8f5615d..6bded009a4 100644
--- a/util/aio-win32.c
+++ b/util/aio-win32.c
@@ -73,15 +73,18 @@ void aio_set_fd_handler(AioContext *ctx,
{
AioHandler *old_node;
AioHandler *node = NULL;
+ SOCKET s;
if (!fd_is_socket(fd)) {
error_report("fd=%d is not a socket, AIO implementation is missing",
fd);
return;
}
+ s = _get_osfhandle(fd);
+
qemu_lockcnt_lock(&ctx->list_lock);
QLIST_FOREACH(old_node, &ctx->aio_handlers, node) {
- if (old_node->pfd.fd == fd && !old_node->deleted) {
+ if (old_node->pfd.fd == s && !old_node->deleted) {
break;
}
}
@@ -92,7 +95,7 @@ void aio_set_fd_handler(AioContext *ctx,
/* Alloc and insert if it's not already there */
node = g_new0(AioHandler, 1);
- node->pfd.fd = fd;
+ node->pfd.fd = s;
node->pfd.events = 0;
if (node->io_read) {
@@ -120,7 +123,7 @@ void aio_set_fd_handler(AioContext *ctx,
QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node);
event = event_notifier_get_handle(&ctx->notifier);
- qemu_socket_select(node->pfd.fd, event, bitmask, NULL);
+ qemu_socket_select(fd, event, bitmask, NULL);
}
if (old_node) {
aio_remove_fd_handler(ctx, old_node);
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index dbd32acc98..7836fb0be3 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -283,13 +283,20 @@ char *qemu_get_pid_name(pid_t pid)
}
-bool qemu_socket_select(SOCKET s, WSAEVENT hEventObject,
+bool qemu_socket_select(int sockfd, WSAEVENT hEventObject,
long lNetworkEvents, Error **errp)
{
+ SOCKET s = _get_osfhandle(sockfd);
+
if (errp == NULL) {
errp = &error_warn;
}
+ if (s == INVALID_SOCKET) {
+ error_setg(errp, "invalid socket fd=%d", sockfd);
+ return false;
+ }
+
if (WSAEventSelect(s, hEventObject, lNetworkEvents) != 0) {
error_setg_win32(errp, WSAGetLastError(), "failed to
WSAEventSelect()");
return false;
@@ -298,9 +305,9 @@ bool qemu_socket_select(SOCKET s, WSAEVENT hEventObject,
return true;
}
-bool qemu_socket_unselect(SOCKET s, Error **errp)
+bool qemu_socket_unselect(int sockfd, Error **errp)
{
- return qemu_socket_select(s, NULL, 0, errp);
+ return qemu_socket_select(sockfd, NULL, 0, errp);
}
#undef connect
@@ -308,7 +315,13 @@ int qemu_connect_wrap(int sockfd, const struct sockaddr
*addr,
socklen_t addrlen)
{
int ret;
- ret = connect(sockfd, addr, addrlen);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = connect(s, addr, addrlen);
if (ret < 0) {
if (WSAGetLastError() == WSAEWOULDBLOCK) {
errno = EINPROGRESS;
@@ -324,7 +337,13 @@ int qemu_connect_wrap(int sockfd, const struct sockaddr
*addr,
int qemu_listen_wrap(int sockfd, int backlog)
{
int ret;
- ret = listen(sockfd, backlog);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = listen(s, backlog);
if (ret < 0) {
errno = socket_error();
}
@@ -337,7 +356,13 @@ int qemu_bind_wrap(int sockfd, const struct sockaddr *addr,
socklen_t addrlen)
{
int ret;
- ret = bind(sockfd, addr, addrlen);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = bind(s, addr, addrlen);
if (ret < 0) {
errno = socket_error();
}
@@ -345,28 +370,108 @@ int qemu_bind_wrap(int sockfd, const struct sockaddr
*addr,
}
-#undef socket
-int qemu_socket_wrap(int domain, int type, int protocol)
+#undef closesocket
+int qemu_closesocket_wrap(int fd)
{
int ret;
- ret = socket(domain, type, protocol);
+ DWORD flags = 0;
+ SOCKET s = _get_osfhandle(fd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ /*
+ * If we were to just call _close on the descriptor, it would close the
+ * HANDLE, but it wouldn't free any of the resources associated to the
+ * SOCKET, and we can't call _close after calling closesocket, because
+ * closesocket has already closed the HANDLE, and _close would attempt to
+ * close the HANDLE again, resulting in a double free. We can however
+ * protect the HANDLE from actually being closed long enough to close the
+ * file descriptor, then close the socket itself.
+ */
+ if (!GetHandleInformation((HANDLE)s, &flags)) {
+ errno = EACCES;
+ return -1;
+ }
+
+ if (!SetHandleInformation((HANDLE)s, HANDLE_FLAG_PROTECT_FROM_CLOSE,
HANDLE_FLAG_PROTECT_FROM_CLOSE)) {
+ errno = EACCES;
+ return -1;
+ }
+
+ ret = close(fd);
+
+ if (!SetHandleInformation((HANDLE)s, flags, flags)) {
+ errno = EACCES;
+ return -1;
+ }
+
+ /*
+ * close() returns EBADF since we PROTECT_FROM_CLOSE the underlying handle,
+ * but the FD is actually freed
+ */
+ if (ret < 0 && errno != EBADF) {
+ return ret;
+ }
+
+ ret = closesocket(s);
if (ret < 0) {
errno = socket_error();
}
+
return ret;
}
+#undef socket
+int qemu_socket_wrap(int domain, int type, int protocol)
+{
+ SOCKET s;
+ int fd;
+
+ s = socket(domain, type, protocol);
+ if (s == -1) {
+ errno = socket_error();
+ return -1;
+ }
+
+ fd = _open_osfhandle(s, _O_BINARY);
+ if (fd < 0) {
+ closesocket(s);
+ /* _open_osfhandle may not set errno, and closesocket() may override
it */
+ errno = ENOMEM;
+ }
+
+ return fd;
+}
+
+
#undef accept
int qemu_accept_wrap(int sockfd, struct sockaddr *addr,
socklen_t *addrlen)
{
- int ret;
- ret = accept(sockfd, addr, addrlen);
- if (ret < 0) {
+ int fd;
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ s = accept(s, addr, addrlen);
+ if (s == -1) {
errno = socket_error();
+ return -1;
}
- return ret;
+
+ fd = _open_osfhandle(s, _O_BINARY);
+ if (fd < 0) {
+ closesocket(s);
+ /* _open_osfhandle may not set errno, and closesocket() may override
it */
+ errno = ENOMEM;
+ }
+
+ return fd;
}
@@ -374,7 +479,13 @@ int qemu_accept_wrap(int sockfd, struct sockaddr *addr,
int qemu_shutdown_wrap(int sockfd, int how)
{
int ret;
- ret = shutdown(sockfd, how);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = shutdown(s, how);
if (ret < 0) {
errno = socket_error();
}
@@ -386,19 +497,13 @@ int qemu_shutdown_wrap(int sockfd, int how)
int qemu_ioctlsocket_wrap(int fd, int req, void *val)
{
int ret;
- ret = ioctlsocket(fd, req, val);
- if (ret < 0) {
- errno = socket_error();
- }
- return ret;
-}
+ SOCKET s = _get_osfhandle(fd);
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
-#undef closesocket
-int qemu_closesocket_wrap(int fd)
-{
- int ret;
- ret = closesocket(fd);
+ ret = ioctlsocket(s, req, val);
if (ret < 0) {
errno = socket_error();
}
@@ -411,7 +516,13 @@ int qemu_getsockopt_wrap(int sockfd, int level, int
optname,
void *optval, socklen_t *optlen)
{
int ret;
- ret = getsockopt(sockfd, level, optname, optval, optlen);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = getsockopt(s, level, optname, optval, optlen);
if (ret < 0) {
errno = socket_error();
}
@@ -424,7 +535,13 @@ int qemu_setsockopt_wrap(int sockfd, int level, int
optname,
const void *optval, socklen_t optlen)
{
int ret;
- ret = setsockopt(sockfd, level, optname, optval, optlen);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = setsockopt(s, level, optname, optval, optlen);
if (ret < 0) {
errno = socket_error();
}
@@ -437,7 +554,13 @@ int qemu_getpeername_wrap(int sockfd, struct sockaddr
*addr,
socklen_t *addrlen)
{
int ret;
- ret = getpeername(sockfd, addr, addrlen);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = getpeername(s, addr, addrlen);
if (ret < 0) {
errno = socket_error();
}
@@ -450,7 +573,13 @@ int qemu_getsockname_wrap(int sockfd, struct sockaddr
*addr,
socklen_t *addrlen)
{
int ret;
- ret = getsockname(sockfd, addr, addrlen);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = getsockname(s, addr, addrlen);
if (ret < 0) {
errno = socket_error();
}
@@ -462,7 +591,13 @@ int qemu_getsockname_wrap(int sockfd, struct sockaddr
*addr,
ssize_t qemu_send_wrap(int sockfd, const void *buf, size_t len, int flags)
{
int ret;
- ret = send(sockfd, buf, len, flags);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = send(s, buf, len, flags);
if (ret < 0) {
errno = socket_error();
}
@@ -475,7 +610,13 @@ ssize_t qemu_sendto_wrap(int sockfd, const void *buf,
size_t len, int flags,
const struct sockaddr *addr, socklen_t addrlen)
{
int ret;
- ret = sendto(sockfd, buf, len, flags, addr, addrlen);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = sendto(s, buf, len, flags, addr, addrlen);
if (ret < 0) {
errno = socket_error();
}
@@ -487,7 +628,13 @@ ssize_t qemu_sendto_wrap(int sockfd, const void *buf,
size_t len, int flags,
ssize_t qemu_recv_wrap(int sockfd, void *buf, size_t len, int flags)
{
int ret;
- ret = recv(sockfd, buf, len, flags);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = recv(s, buf, len, flags);
if (ret < 0) {
errno = socket_error();
}
@@ -500,7 +647,13 @@ ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t
len, int flags,
struct sockaddr *addr, socklen_t *addrlen)
{
int ret;
- ret = recvfrom(sockfd, buf, len, flags, addr, addrlen);
+ SOCKET s = _get_osfhandle(sockfd);
+
+ if (s == INVALID_SOCKET) {
+ return -1;
+ }
+
+ ret = recvfrom(s, buf, len, flags, addr, addrlen);
if (ret < 0) {
errno = socket_error();
}
--
2.39.2
- [PULL v2 06/25] win32/socket: introduce qemu_socket_select() helper, (continued)
- [PULL v2 06/25] win32/socket: introduce qemu_socket_select() helper, marcandre . lureau, 2023/03/13
- [PULL v2 07/25] win32/socket: introduce qemu_socket_unselect() helper, marcandre . lureau, 2023/03/13
- [PULL v2 05/25] error: add global &error_warn destination, marcandre . lureau, 2023/03/13
- [PULL v2 08/25] aio: make aio_set_fd_poll() static to aio-posix.c, marcandre . lureau, 2023/03/13
- [PULL v2 09/25] aio/win32: aio_set_fd_handler() only supports SOCKET, marcandre . lureau, 2023/03/13
- [PULL v2 10/25] main-loop: remove qemu_fd_register(), win32/slirp/socket specific, marcandre . lureau, 2023/03/13
- [PULL v2 11/25] slirp: unregister the win32 SOCKET, marcandre . lureau, 2023/03/13
- [PULL v2 12/25] slirp: open-code qemu_socket_(un)select(), marcandre . lureau, 2023/03/13
- [PULL v2 14/25] os-posix: remove useless ioctlsocket() define, marcandre . lureau, 2023/03/13
- [PULL v2 15/25] win32: replace closesocket() with close() wrapper, marcandre . lureau, 2023/03/13
- [PULL v2 13/25] win32: avoid mixing SOCKET and file descriptor space,
marcandre . lureau <=
- [PULL v2 17/25] char: do not double-close fd when failing to add client, marcandre . lureau, 2023/03/13
- [PULL v2 19/25] osdep: implement qemu_socketpair() for win32, marcandre . lureau, 2023/03/13
- [PULL v2 16/25] tests: fix path separator, use g_build_filename(), marcandre . lureau, 2023/03/13
- [PULL v2 21/25] monitor: release the lock before calling close(), marcandre . lureau, 2023/03/13
- [PULL v2 22/25] qmp: add 'get-win32-socket', marcandre . lureau, 2023/03/13
- [PULL v2 20/25] qmp: 'add_client' actually expects sockets, marcandre . lureau, 2023/03/13
- [PULL v2 18/25] tests/docker: fix a win32 error due to portability, marcandre . lureau, 2023/03/13
- [PULL v2 24/25] qtest: enable vnc-display test on win32, marcandre . lureau, 2023/03/13
- [PULL v2 23/25] libqtest: make qtest_qmp_add_client work on win32, marcandre . lureau, 2023/03/13
- [PULL v2 25/25] monitor: restrict command getfd to POSIX hosts, marcandre . lureau, 2023/03/13