diff options
38 files changed, 701 insertions, 258 deletions
diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index d18144b..402a2d6 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -573,13 +573,13 @@ static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) goto err_exit; } - closesocket(fds[1]); + close(fds[1]); return 0; err_exit: - closesocket(fds[0]); - closesocket(fds[1]); + close(fds[0]); + close(fds[1]); return -1; } diff --git a/chardev/char.c b/chardev/char.c index 11eab77..e693906 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -1175,12 +1175,10 @@ bool qmp_add_client_char(int fd, bool has_skipauth, bool skipauth, if (!s) { error_setg(errp, "protocol '%s' is invalid", protocol); - close(fd); return false; } if (qemu_chr_add_client(s, fd) < 0) { error_setg(errp, "failed to add client"); - close(fd); return false; } return true; diff --git a/crypto/afalg.c b/crypto/afalg.c index 10046bb..348301e 100644 --- a/crypto/afalg.c +++ b/crypto/afalg.c @@ -59,7 +59,7 @@ qcrypto_afalg_socket_bind(const char *type, const char *name, if (bind(sbind, (const struct sockaddr *)&salg, sizeof(salg)) != 0) { error_setg_errno(errp, errno, "Failed to bind socket"); - closesocket(sbind); + close(sbind); return -1; } @@ -105,11 +105,11 @@ void qcrypto_afalg_comm_free(QCryptoAFAlg *afalg) } if (afalg->tfmfd != -1) { - closesocket(afalg->tfmfd); + close(afalg->tfmfd); } if (afalg->opfd != -1) { - closesocket(afalg->opfd); + close(afalg->opfd); } g_free(afalg); diff --git a/hmp-commands.hx b/hmp-commands.hx index b87c250..bb85ee1 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1486,6 +1486,7 @@ SRST Inject an MCE on the given CPU (x86 only). ERST +#ifdef CONFIG_POSIX { .name = "getfd", .args_type = "fdname:s", @@ -1501,6 +1502,7 @@ SRST mechanism on unix sockets, it is stored using the name *fdname* for later use by other monitor commands. ERST +#endif { .name = "closefd", diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index 94fe1b5..065e12f 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -340,7 +340,7 @@ static void hv_syndbg_realize(DeviceState *dev, Error **errp) syndbg->servaddr.sin_family = AF_INET; if (connect(syndbg->socket, (struct sockaddr *)&syndbg->servaddr, sizeof(syndbg->servaddr)) < 0) { - closesocket(syndbg->socket); + close(syndbg->socket); error_setg(errp, "%s failed to connect to socket", TYPE_HV_SYNDBG); return; } @@ -357,7 +357,7 @@ static void hv_syndbg_unrealize(DeviceState *dev) if (syndbg->socket > 0) { qemu_set_fd_handler(syndbg->socket, NULL, NULL, NULL); - closesocket(syndbg->socket); + close(syndbg->socket); } } diff --git a/include/block/aio.h b/include/block/aio.h index 8fba6a3..543717f 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -482,14 +482,6 @@ void aio_set_fd_handler(AioContext *ctx, IOHandler *io_poll_ready, void *opaque); -/* Set polling begin/end callbacks for a file descriptor that has already been - * registered with aio_set_fd_handler. Do nothing if the file descriptor is - * not registered. - */ -void aio_set_fd_poll(AioContext *ctx, int fd, - IOHandler *io_poll_begin, - IOHandler *io_poll_end); - /* Register an event notifier and associated callbacks. Behaves very similarly * to event_notifier_set_handler. Unlike event_notifier_set_handler, these callbacks * will be invoked when using aio_poll(). diff --git a/include/qapi/error.h b/include/qapi/error.h index d798fae..f21a231 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -520,6 +520,12 @@ static inline void error_propagator_cleanup(ErrorPropagator *prop) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup); /* + * Special error destination to warn on error. + * See error_setg() and error_propagate() for details. + */ +extern Error *error_warn; + +/* * Special error destination to abort on error. * See error_setg() and error_propagate() for details. */ diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index c25f390..b3e54e0 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -387,8 +387,6 @@ void qemu_cond_timedwait_iothread(QemuCond *cond, int ms); /* internal interfaces */ -void qemu_fd_register(int fd); - #define qemu_bh_new(cb, opaque) \ qemu_bh_new_full((cb), (opaque), (stringify(cb))) QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name); diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 88c9fac..f68b5d8 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -665,20 +665,6 @@ void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, */ char *qemu_get_pid_name(pid_t pid); -/** - * qemu_fork: - * - * A version of fork that avoids signal handler race - * conditions that can lead to child process getting - * signals that are otherwise only expected by the - * parent. It also resets all signal handlers to the - * default settings. - * - * Returns 0 to child process, pid number to parent - * or -1 on failure. - */ -pid_t qemu_fork(Error **errp); - /* Using intptr_t ensures that qemu_*_page_mask is sign-extended even * when intptr_t is 32-bit and we are aligning a long long. */ diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 2b0698a..d935fd8 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -15,7 +15,6 @@ int inet_aton(const char *cp, struct in_addr *ia); bool fd_is_socket(int fd); int qemu_socket(int domain, int type, int protocol); -#ifndef WIN32 /** * qemu_socketpair: * @domain: specifies a communication domain, such as PF_UNIX @@ -30,7 +29,6 @@ int qemu_socket(int domain, int type, int protocol); * Return 0 on success. */ int qemu_socketpair(int domain, int type, int protocol, int sv[2]); -#endif int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); /* diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h index 58de7c9..1030d39 100644 --- a/include/sysemu/os-posix.h +++ b/include/sysemu/os-posix.h @@ -51,9 +51,6 @@ void os_daemonize(void); void os_setup_post(void); int os_mlock(void); -#define closesocket(s) close(s) -#define ioctlsocket(s, r, v) ioctl(s, r, v) - int os_set_daemonize(bool d); bool is_daemonized(void); diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h index 97d0243..e2849f8 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -29,6 +29,7 @@ #include <winsock2.h> #include <windows.h> #include <ws2tcpip.h> +#include "qemu/typedefs.h" #ifdef HAVE_AFUNIX_H #include <afunix.h> @@ -164,10 +165,20 @@ static inline void qemu_funlockfile(FILE *f) #endif } +/* Helper for WSAEventSelect, to report errors */ +bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents, 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() */ +#undef close +#define close qemu_close_wrap +int qemu_close_wrap(int fd); + #undef connect #define connect qemu_connect_wrap int qemu_connect_wrap(int sockfd, const struct sockaddr *addr, @@ -199,10 +210,6 @@ int qemu_shutdown_wrap(int sockfd, int how); #define ioctlsocket qemu_ioctlsocket_wrap int qemu_ioctlsocket_wrap(int fd, int req, void *val); -#undef closesocket -#define closesocket qemu_closesocket_wrap -int qemu_closesocket_wrap(int fd); - #undef getsockopt #define getsockopt qemu_getsockopt_wrap int qemu_getsockopt_wrap(int sockfd, int level, int optname, diff --git a/io/channel-socket.c b/io/channel-socket.c index 7aca84f..b0ea7d4 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -442,9 +442,9 @@ static void qio_channel_socket_finalize(Object *obj) } } #ifdef WIN32 - WSAEventSelect(ioc->fd, NULL, 0); + qemu_socket_unselect(ioc->fd, NULL); #endif - closesocket(ioc->fd); + close(ioc->fd); ioc->fd = -1; } } @@ -846,13 +846,13 @@ qio_channel_socket_close(QIOChannel *ioc, if (sioc->fd != -1) { #ifdef WIN32 - WSAEventSelect(sioc->fd, NULL, 0); + qemu_socket_unselect(sioc->fd, NULL); #endif if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_LISTEN)) { socket_listen_cleanup(sioc->fd, errp); } - if (closesocket(sioc->fd) < 0) { + if (close(sioc->fd) < 0) { sioc->fd = -1; error_setg_errno(&err, errno, "Unable to close socket"); error_propagate(errp, err); diff --git a/io/channel-watch.c b/io/channel-watch.c index ad7c568..64b486e 100644 --- a/io/channel-watch.c +++ b/io/channel-watch.c @@ -275,15 +275,15 @@ 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; - WSAEventSelect(socket, ioc->event, - FD_READ | FD_ACCEPT | FD_CLOSE | - FD_CONNECT | FD_WRITE | FD_OOB); + qemu_socket_select(sockfd, ioc->event, + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB, NULL); source = g_source_new(&qio_channel_socket_source_funcs, sizeof(QIOChannelSocketSource)); @@ -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/monitor/fds.c b/monitor/fds.c index 26b39a0..d86c2c6 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -61,45 +61,59 @@ struct MonFdset { static QemuMutex mon_fdsets_lock; static QLIST_HEAD(, MonFdset) mon_fdsets; -void qmp_getfd(const char *fdname, Error **errp) +static bool monitor_add_fd(Monitor *mon, int fd, const char *fdname, Error **errp) { - Monitor *cur_mon = monitor_cur(); mon_fd_t *monfd; - int fd, tmp_fd; - - fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); - if (fd == -1) { - error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); - return; - } if (qemu_isdigit(fdname[0])) { close(fd); error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname", "a name not starting with a digit"); - return; + return false; } - QEMU_LOCK_GUARD(&cur_mon->mon_lock); - QLIST_FOREACH(monfd, &cur_mon->fds, next) { + /* See close() call below. */ + qemu_mutex_lock(&mon->mon_lock); + QLIST_FOREACH(monfd, &mon->fds, next) { + int tmp_fd; + if (strcmp(monfd->name, fdname) != 0) { continue; } tmp_fd = monfd->fd; monfd->fd = fd; + qemu_mutex_unlock(&mon->mon_lock); /* Make sure close() is outside critical section */ close(tmp_fd); - return; + return true; } monfd = g_new0(mon_fd_t, 1); monfd->name = g_strdup(fdname); monfd->fd = fd; - QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next); + QLIST_INSERT_HEAD(&mon->fds, monfd, next); + qemu_mutex_unlock(&mon->mon_lock); + return true; } +#ifdef CONFIG_POSIX +void qmp_getfd(const char *fdname, Error **errp) +{ + Monitor *cur_mon = monitor_cur(); + int fd; + + fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); + if (fd == -1) { + error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); + return; + } + + monitor_add_fd(cur_mon, fd, fdname, errp); +} +#endif + void qmp_closefd(const char *fdname, Error **errp) { Monitor *cur_mon = monitor_cur(); @@ -211,6 +225,41 @@ error: return NULL; } +#ifdef WIN32 +void qmp_get_win32_socket(const char *infos, const char *fdname, Error **errp) +{ + g_autofree WSAPROTOCOL_INFOW *info = NULL; + gsize len; + SOCKET sk; + int fd; + + info = (void *)g_base64_decode(infos, &len); + if (len != sizeof(*info)) { + error_setg(errp, "Invalid WSAPROTOCOL_INFOW value"); + return; + } + + sk = WSASocketW(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + info, 0, 0); + if (sk == INVALID_SOCKET) { + error_setg_win32(errp, WSAGetLastError(), "Couldn't import socket"); + return; + } + + fd = _open_osfhandle(sk, _O_BINARY); + if (fd < 0) { + error_setg_errno(errp, errno, "Failed to associate a FD with the SOCKET"); + closesocket(sk); + return; + } + + monitor_add_fd(monitor_cur(), fd, fdname, errp); +} +#endif + + void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) { MonFdset *mon_fdset; diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 34bd8c6..6c559b4 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -192,6 +192,7 @@ void hmp_change(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } +#ifdef CONFIG_POSIX void hmp_getfd(Monitor *mon, const QDict *qdict) { const char *fdname = qdict_get_str(qdict, "fdname"); @@ -200,6 +201,7 @@ void hmp_getfd(Monitor *mon, const QDict *qdict) qmp_getfd(fdname, &err); hmp_handle_error(mon, err); } +#endif void hmp_closefd(Monitor *mon, const QDict *qdict) { diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 859012a..b0f948d 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -14,6 +14,7 @@ */ #include "qemu/osdep.h" +#include "qemu/sockets.h" #include "monitor-internal.h" #include "monitor/qdev.h" #include "monitor/qmp-helpers.h" @@ -139,6 +140,12 @@ void qmp_add_client(const char *protocol, const char *fdname, return; } + if (!fd_is_socket(fd)) { + error_setg(errp, "parameter @fdname must name a socket"); + close(fd); + return; + } + for (i = 0; i < ARRAY_SIZE(protocol_table); i++) { if (!strcmp(protocol, protocol_table[i].name)) { if (!protocol_table[i].add_client(fd, has_skipauth, skipauth, diff --git a/net/dgram.c b/net/dgram.c index 9f7bf38..48f653b 100644 --- a/net/dgram.c +++ b/net/dgram.c @@ -230,7 +230,7 @@ static int net_dgram_mcast_create(struct sockaddr_in *mcastaddr, return fd; fail: if (fd >= 0) { - closesocket(fd); + close(fd); } return -1; } @@ -352,7 +352,7 @@ static int net_dgram_mcast_init(NetClientState *peer, if (convert_host_port(saddr, local->u.inet.host, local->u.inet.port, errp) < 0) { g_free(saddr); - closesocket(fd); + close(fd); return -1; } @@ -360,14 +360,14 @@ static int net_dgram_mcast_init(NetClientState *peer, if (saddr->sin_addr.s_addr == 0) { error_setg(errp, "can't setup multicast destination address"); g_free(saddr); - closesocket(fd); + close(fd); return -1; } /* clone dgram socket */ newfd = net_dgram_mcast_create(saddr, NULL, errp); if (newfd < 0) { g_free(saddr); - closesocket(fd); + close(fd); return -1; } /* clone newfd to fd, close newfd */ @@ -494,14 +494,14 @@ int net_init_dgram(const Netdev *netdev, const char *name, if (ret < 0) { error_setg_errno(errp, errno, "can't set socket option SO_REUSEADDR"); - closesocket(fd); + close(fd); return -1; } ret = bind(fd, (struct sockaddr *)&laddr_in, sizeof(laddr_in)); if (ret < 0) { error_setg_errno(errp, errno, "can't bind ip=%s to socket", inet_ntoa(laddr_in.sin_addr)); - closesocket(fd); + close(fd); return -1; } qemu_socket_set_nonblock(fd); @@ -548,7 +548,7 @@ int net_init_dgram(const Netdev *netdev, const char *name, if (ret < 0) { error_setg_errno(errp, errno, "can't bind unix=%s to socket", laddr_un.sun_path); - closesocket(fd); + close(fd); return -1; } qemu_socket_set_nonblock(fd); diff --git a/net/slirp.c b/net/slirp.c index 2ee3f1a..c33b3e0 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -248,12 +248,24 @@ static void net_slirp_timer_mod(void *timer, int64_t expire_timer, static void net_slirp_register_poll_fd(int fd, void *opaque) { - qemu_fd_register(fd); +#ifdef WIN32 + AioContext *ctxt = qemu_get_aio_context(); + + if (WSAEventSelect(fd, event_notifier_get_handle(&ctxt->notifier), + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB) != 0) { + error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()"); + } +#endif } static void net_slirp_unregister_poll_fd(int fd, void *opaque) { - /* no qemu_fd_unregister */ +#ifdef WIN32 + if (WSAEventSelect(fd, NULL, 0) != 0) { + error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()"); + } +#endif } static void net_slirp_notify(void *opaque) diff --git a/net/socket.c b/net/socket.c index 2fc5696..ba6e5b0 100644 --- a/net/socket.c +++ b/net/socket.c @@ -172,7 +172,7 @@ static void net_socket_send(void *opaque) if (s->listen_fd != -1) { qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s); } - closesocket(s->fd); + close(s->fd); s->fd = -1; net_socket_rs_init(&s->rs, net_socket_rs_finalize, false); @@ -299,7 +299,7 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, return fd; fail: if (fd >= 0) - closesocket(fd); + close(fd); return -1; } @@ -314,7 +314,7 @@ static void net_socket_cleanup(NetClientState *nc) } if (s->listen_fd != -1) { qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); - closesocket(s->listen_fd); + close(s->listen_fd); s->listen_fd = -1; } } @@ -399,7 +399,7 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, return s; err: - closesocket(fd); + close(fd); return NULL; } @@ -456,7 +456,7 @@ static NetSocketState *net_socket_fd_init(NetClientState *peer, if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, (socklen_t *)&optlen)< 0) { error_setg(errp, "can't get socket option SO_TYPE"); - closesocket(fd); + close(fd); return NULL; } switch(so_type) { @@ -468,7 +468,7 @@ static NetSocketState *net_socket_fd_init(NetClientState *peer, default: error_setg(errp, "socket type=%d for fd=%d must be either" " SOCK_DGRAM or SOCK_STREAM", so_type, fd); - closesocket(fd); + close(fd); } return NULL; } @@ -526,13 +526,13 @@ static int net_socket_listen_init(NetClientState *peer, if (ret < 0) { error_setg_errno(errp, errno, "can't bind ip=%s to socket", inet_ntoa(saddr.sin_addr)); - closesocket(fd); + close(fd); return -1; } ret = listen(fd, 0); if (ret < 0) { error_setg_errno(errp, errno, "can't listen on socket"); - closesocket(fd); + close(fd); return -1; } @@ -579,7 +579,7 @@ static int net_socket_connect_init(NetClientState *peer, break; } else { error_setg_errno(errp, errno, "can't connect socket"); - closesocket(fd); + close(fd); return -1; } } else { @@ -671,14 +671,14 @@ static int net_socket_udp_init(NetClientState *peer, if (ret < 0) { error_setg_errno(errp, errno, "can't set socket option SO_REUSEADDR"); - closesocket(fd); + close(fd); return -1; } ret = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr)); if (ret < 0) { error_setg_errno(errp, errno, "can't bind ip=%s to socket", inet_ntoa(laddr.sin_addr)); - closesocket(fd); + close(fd); return -1; } qemu_socket_set_nonblock(fd); diff --git a/qapi/misc.json b/qapi/misc.json index 27ef5a2..6ddd16e 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -14,6 +14,9 @@ # Allow client connections for VNC, Spice and socket based # character devices to be passed in to QEMU via SCM_RIGHTS. # +# If the FD associated with @fdname is not a socket, the command will fail and +# the FD will be closed. +# # @protocol: protocol name. Valid names are "vnc", "spice", "@dbus-display" or # the name of a character device (eg. from -chardev id=XXXX) # @@ -270,7 +273,38 @@ # <- { "return": {} } # ## -{ 'command': 'getfd', 'data': {'fdname': 'str'} } +{ 'command': 'getfd', 'data': {'fdname': 'str'}, 'if': 'CONFIG_POSIX' } + +## +# @get-win32-socket: +# +# Add a socket that was duplicated to QEMU process with +# WSADuplicateSocketW() via WSASocket() & WSAPROTOCOL_INFOW structure +# and assign it a name (the SOCKET is associated with a CRT file +# descriptor) +# +# @info: the WSAPROTOCOL_INFOW structure (encoded in base64) +# +# @fdname: file descriptor name +# +# Returns: Nothing on success +# +# Since: 8.0 +# +# Notes: If @fdname already exists, the file descriptor assigned to +# it will be closed and replaced by the received file +# descriptor. +# +# The 'closefd' command can be used to explicitly close the +# file descriptor when it is no longer needed. +# +# Example: +# +# -> { "execute": "get-win32-socket", "arguments": { "info": "abcd123..", fdname": "skclient" } } +# <- { "return": {} } +# +## +{ 'command': 'get-win32-socket', 'data': {'info': 'str', 'fdname': 'str'}, 'if': 'CONFIG_WIN32' } ## # @closefd: diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 3a1ed7c..688ef62 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -23,10 +23,10 @@ import enum import tempfile import re import signal +import getpass from tarfile import TarFile, TarInfo from io import StringIO, BytesIO from shutil import copy, rmtree -from pwd import getpwuid from datetime import datetime, timedelta @@ -316,7 +316,7 @@ class Docker(object): if user: uid = os.getuid() - uname = getpwuid(uid).pw_name + uname = getpass.getuser() tmp_df.write("\n") tmp_df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" % (uname, uid, uname)) @@ -570,7 +570,7 @@ class UpdateCommand(SubCommand): if args.user: uid = os.getuid() - uname = getpwuid(uid).pw_name + uname = getpass.getuser() df.write("\n") df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" % (uname, uid, uname)) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 2bfd460..c3a0ef5 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -124,7 +124,7 @@ static int socket_accept(int sock) (void *)&timeout, sizeof(timeout))) { fprintf(stderr, "%s failed to set SO_RCVTIMEO: %s\n", __func__, strerror(errno)); - closesocket(sock); + close(sock); return -1; } @@ -135,7 +135,7 @@ static int socket_accept(int sock) if (ret == -1) { fprintf(stderr, "%s failed: %s\n", __func__, strerror(errno)); } - closesocket(sock); + close(sock); return ret; } @@ -564,8 +564,8 @@ void qtest_quit(QTestState *s) qtest_remove_abrt_handler(s); qtest_kill_qemu(s); - closesocket(s->fd); - closesocket(s->qmp_fd); + close(s->fd); + close(s->qmp_fd); g_string_free(s->rx, true); for (GList *it = s->pending_events; it != NULL; it = it->next) { @@ -1478,13 +1478,28 @@ void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, qobject_unref(args); } -#ifndef _WIN32 void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd) { QDict *resp; +#ifdef WIN32 + WSAPROTOCOL_INFOW info; + g_autofree char *info64 = NULL; + SOCKET s; + + assert(fd_is_socket(fd)); + s = _get_osfhandle(fd); + if (WSADuplicateSocketW(s, GetProcessId((HANDLE)qts->qemu_pid), &info) == SOCKET_ERROR) { + g_autofree char *emsg = g_win32_error_message(WSAGetLastError()); + g_error("WSADuplicateSocketW failed: %s", emsg); + } + info64 = g_base64_encode((guchar *)&info, sizeof(info)); + resp = qtest_qmp(qts, "{'execute': 'get-win32-socket'," + "'arguments': {'fdname': 'fdname', 'info': %s}}", info64); +#else resp = qtest_qmp_fds(qts, &fd, 1, "{'execute': 'getfd'," "'arguments': {'fdname': 'fdname'}}"); +#endif g_assert(resp); g_assert(!qdict_haskey(resp, "event")); /* We don't expect any events */ g_assert(!qdict_haskey(resp, "error")); @@ -1498,7 +1513,6 @@ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd) g_assert(!qdict_haskey(resp, "error")); qobject_unref(resp); } -#endif /* * Generic hot-unplugging test via the device_del QMP command. diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index fcf1c3c..8d7d450 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -758,17 +758,16 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, const char *fmt, ...) G_GNUC_PRINTF(4, 5); -#ifndef _WIN32 /** * qtest_qmp_add_client: * @qts: QTestState instance to operate on * @protocol: the protocol to add to * @fd: the client file-descriptor * - * Call QMP ``getfd`` followed by ``add_client`` with the given @fd. + * Call QMP ``getfd`` (on Windows ``get-win32-socket``) followed by + * ``add_client`` with the given @fd. */ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd); -#endif /* _WIN32 */ /** * qtest_qmp_device_del_send: diff --git a/tests/qtest/microbit-test.c b/tests/qtest/microbit-test.c index 4bc2670..6022a92 100644 --- a/tests/qtest/microbit-test.c +++ b/tests/qtest/microbit-test.c @@ -107,7 +107,7 @@ static void test_nrf51_uart(void) g_assert_true(recv(sock_fd, s, 10, 0) == 5); g_assert_true(memcmp(s, "world", 5) == 0); - closesocket(sock_fd); + close(sock_fd); qtest_quit(qts); } diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c index 270e424..9cf1b06 100644 --- a/tests/qtest/netdev-socket.c +++ b/tests/qtest/netdev-socket.c @@ -99,7 +99,7 @@ static int inet_get_free_port_multiple(int nb, int *port, bool ipv6) nb = i; for (i = 0; i < nb; i++) { - closesocket(sock[i]); + close(sock[i]); } return nb; @@ -361,8 +361,8 @@ static void test_stream_fd(void) qtest_quit(qts1); qtest_quit(qts0); - closesocket(sock[0]); - closesocket(sock[1]); + close(sock[0]); + close(sock[1]); } #endif @@ -487,8 +487,8 @@ static void test_dgram_fd(void) qtest_quit(qts1); qtest_quit(qts0); - closesocket(sv[0]); - closesocket(sv[1]); + close(sv[0]); + close(sv[1]); } #endif diff --git a/tests/qtest/vnc-display-test.c b/tests/qtest/vnc-display-test.c index e52a432..f8933b0 100644 --- a/tests/qtest/vnc-display-test.c +++ b/tests/qtest/vnc-display-test.c @@ -19,7 +19,7 @@ typedef struct Test { GMainLoop *loop; } Test; -#if !defined(WIN32) && !defined(CONFIG_DARWIN) +#if !defined(CONFIG_DARWIN) static void on_vnc_error(VncConnection* self, const char* msg) @@ -38,10 +38,7 @@ static void on_vnc_auth_failure(VncConnection *self, static bool test_setup(Test *test) { -#ifdef WIN32 - g_test_skip("Not supported on Windows yet"); - return false; -#elif defined(CONFIG_DARWIN) +#if defined(CONFIG_DARWIN) g_test_skip("Broken on Darwin"); return false; #else @@ -59,7 +56,12 @@ test_setup(Test *test) g_signal_connect(test->conn, "vnc-auth-failure", G_CALLBACK(on_vnc_auth_failure), NULL); vnc_connection_set_auth_type(test->conn, VNC_CONNECTION_AUTH_NONE); + +#ifdef WIN32 + vnc_connection_open_fd(test->conn, _get_osfhandle(pair[0])); +#else vnc_connection_open_fd(test->conn, pair[0]); +#endif test->loop = g_main_loop_new(NULL, FALSE); return true; diff --git a/tests/unit/meson.build b/tests/unit/meson.build index d9c0a7e..fa63cfe 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -11,6 +11,7 @@ tests = { 'check-qobject': [], 'check-qjson': [], 'check-qlit': [], + 'test-error-report': [], 'test-qobject-output-visitor': [testqapi], 'test-clone-visitor': [testqapi], 'test-qobject-input-visitor': [testqapi], diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c index eecadf3..6de27ba 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -160,7 +160,7 @@ void socket_check_afunix_support(bool *has_afunix) int fd; fd = socket(PF_UNIX, SOCK_STREAM, 0); - closesocket(fd); + close(fd); #ifdef _WIN32 *has_afunix = (fd != (int)INVALID_SOCKET); diff --git a/tests/unit/test-error-report.c b/tests/unit/test-error-report.c new file mode 100644 index 0000000..54319c8 --- /dev/null +++ b/tests/unit/test-error-report.c @@ -0,0 +1,139 @@ +/* + * Error reporting test + * + * Copyright (C) 2022 Red Hat Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "glib-compat.h" +#include <locale.h> + +#include "qemu/error-report.h" +#include "qapi/error.h" + +static void +test_error_report_simple(void) +{ + if (g_test_subprocess()) { + error_report("%s", "test error"); + warn_report("%s", "test warn"); + info_report("%s", "test info"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: test error*\ +test-error-report: warning: test warn*\ +test-error-report: info: test info*\ +"); +} + +static void +test_error_report_loc(void) +{ + if (g_test_subprocess()) { + loc_set_file("some-file.c", 7717); + error_report("%s", "test error1"); + loc_set_none(); + error_report("%s", "test error2"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report:some-file.c:7717: test error1*\ +test-error-report: test error2*\ +"); +} + +static void +test_error_report_glog(void) +{ + if (g_test_subprocess()) { + g_message("gmessage"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("test-error-report: info: gmessage*"); +} + +static void +test_error_report_once(void) +{ + int i; + + if (g_test_subprocess()) { + for (i = 0; i < 3; i++) { + warn_report_once("warn"); + error_report_once("err"); + } + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: warning: warn*\ +test-error-report: err*\ +"); +} + +static void +test_error_report_timestamp(void) +{ + if (g_test_subprocess()) { + message_with_timestamp = true; + warn_report("warn"); + error_report("err"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +*-*-*:*:* test-error-report: warning: warn*\ +*-*-*:*:* test-error-report: err*\ +"); +} + +static void +test_error_warn(void) +{ + if (g_test_subprocess()) { + error_setg(&error_warn, "Testing &error_warn"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: warning: Testing &error_warn*\ +"); +} + + +int +main(int argc, char *argv[]) +{ + setlocale(LC_ALL, ""); + + g_test_init(&argc, &argv, NULL); + error_init("test-error-report"); + + g_test_add_func("/error-report/simple", test_error_report_simple); + g_test_add_func("/error-report/loc", test_error_report_loc); + g_test_add_func("/error-report/glog", test_error_report_glog); + g_test_add_func("/error-report/once", test_error_report_once); + g_test_add_func("/error-report/timestamp", test_error_report_timestamp); + g_test_add_func("/error-report/warn", test_error_warn); + + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-command.c b/tests/unit/test-io-channel-command.c index c6e66a8..4f02261 100644 --- a/tests/unit/test-io-channel-command.c +++ b/tests/unit/test-io-channel-command.c @@ -35,7 +35,7 @@ static char *socat = NULL; static void test_io_channel_command_fifo(bool async) { g_autofree gchar *tmpdir = g_dir_make_tmp("qemu-test-io-channel.XXXXXX", NULL); - g_autofree gchar *fifo = g_strdup_printf("%s/%s", tmpdir, TEST_FIFO); + g_autofree gchar *fifo = g_build_filename(tmpdir, TEST_FIFO, NULL); g_autofree gchar *srcargs = g_strdup_printf("%s - PIPE:%s,wronly", socat, fifo); g_autofree gchar *dstargs = g_strdup_printf("%s PIPE:%s,rdonly -", socat, fifo); g_auto(GStrv) srcargv = g_strsplit(srcargs, " ", -1); diff --git a/util/aio-posix.c b/util/aio-posix.c index 6cc6256..a8be940 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -180,9 +180,9 @@ void aio_set_fd_handler(AioContext *ctx, } } -void aio_set_fd_poll(AioContext *ctx, int fd, - IOHandler *io_poll_begin, - IOHandler *io_poll_end) +static void aio_set_fd_poll(AioContext *ctx, int fd, + IOHandler *io_poll_begin, + IOHandler *io_poll_end) { AioHandler *node = find_aio_handler(ctx, fd); diff --git a/util/aio-win32.c b/util/aio-win32.c index 80cfe01..6bded00 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -22,6 +22,7 @@ #include "qemu/sockets.h" #include "qapi/error.h" #include "qemu/rcu_queue.h" +#include "qemu/error-report.h" struct AioHandler { EventNotifier *e; @@ -70,13 +71,20 @@ void aio_set_fd_handler(AioContext *ctx, IOHandler *io_poll_ready, void *opaque) { - /* fd is a SOCKET in our case */ 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; } } @@ -87,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) { @@ -115,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); - WSAEventSelect(node->pfd.fd, event, bitmask); + qemu_socket_select(fd, event, bitmask, NULL); } if (old_node) { aio_remove_fd_handler(ctx, old_node); @@ -125,13 +133,6 @@ void aio_set_fd_handler(AioContext *ctx, aio_notify(ctx); } -void aio_set_fd_poll(AioContext *ctx, int fd, - IOHandler *io_poll_begin, - IOHandler *io_poll_end) -{ - /* Not implemented */ -} - void aio_set_event_notifier(AioContext *ctx, EventNotifier *e, bool is_external, diff --git a/util/error.c b/util/error.c index 1e7af66..5537245 100644 --- a/util/error.c +++ b/util/error.c @@ -27,8 +27,9 @@ struct Error Error *error_abort; Error *error_fatal; +Error *error_warn; -static void error_handle_fatal(Error **errp, Error *err) +static void error_handle(Error **errp, Error *err) { if (errp == &error_abort) { fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", @@ -43,6 +44,9 @@ static void error_handle_fatal(Error **errp, Error *err) error_report_err(err); exit(1); } + if (errp == &error_warn) { + warn_report_err(err); + } } G_GNUC_PRINTF(6, 0) @@ -71,7 +75,7 @@ static void error_setv(Error **errp, err->line = line; err->func = func; - error_handle_fatal(errp, err); + error_handle(errp, err); *errp = err; errno = saved_errno; @@ -284,7 +288,7 @@ void error_propagate(Error **dst_errp, Error *local_err) if (!local_err) { return; } - error_handle_fatal(dst_errp, local_err); + error_handle(dst_errp, local_err); if (dst_errp && !*dst_errp) { *dst_errp = local_err; } else { diff --git a/util/main-loop.c b/util/main-loop.c index 3c0f525..e180c85 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -252,10 +252,6 @@ static int max_priority; static int glib_pollfds_idx; static int glib_n_poll_fds; -void qemu_fd_register(int fd) -{ -} - static void glib_pollfds_fill(int64_t *cur_timeout) { GMainContext *context = g_main_context_default(); @@ -414,13 +410,6 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) } } -void qemu_fd_register(int fd) -{ - WSAEventSelect(fd, event_notifier_get_handle(&qemu_aio_context->notifier), - FD_READ | FD_ACCEPT | FD_CLOSE | - FD_CONNECT | FD_WRITE | FD_OOB); -} - static int pollfds_fill(GArray *pollfds, fd_set *rfds, fd_set *wfds, fd_set *xfds) { diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 77d882e..760390b 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -583,76 +583,6 @@ char *qemu_get_pid_name(pid_t pid) } -pid_t qemu_fork(Error **errp) -{ - sigset_t oldmask, newmask; - struct sigaction sig_action; - int saved_errno; - pid_t pid; - - /* - * Need to block signals now, so that child process can safely - * kill off caller's signal handlers without a race. - */ - sigfillset(&newmask); - if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) { - error_setg_errno(errp, errno, - "cannot block signals"); - return -1; - } - - pid = fork(); - saved_errno = errno; - - if (pid < 0) { - /* attempt to restore signal mask, but ignore failure, to - * avoid obscuring the fork failure */ - (void)pthread_sigmask(SIG_SETMASK, &oldmask, NULL); - error_setg_errno(errp, saved_errno, - "cannot fork child process"); - errno = saved_errno; - return -1; - } else if (pid) { - /* parent process */ - - /* Restore our original signal mask now that the child is - * safely running. Only documented failures are EFAULT (not - * possible, since we are using just-grabbed mask) or EINVAL - * (not possible, since we are using correct arguments). */ - (void)pthread_sigmask(SIG_SETMASK, &oldmask, NULL); - } else { - /* child process */ - size_t i; - - /* Clear out all signal handlers from parent so nothing - * unexpected can happen in our child once we unblock - * signals */ - sig_action.sa_handler = SIG_DFL; - sig_action.sa_flags = 0; - sigemptyset(&sig_action.sa_mask); - - for (i = 1; i < NSIG; i++) { - /* Only possible errors are EFAULT or EINVAL The former - * won't happen, the latter we expect, so no need to check - * return value */ - (void)sigaction(i, &sig_action, NULL); - } - - /* Unmask all signals in child, since we've no idea what the - * caller's done with their signal mask and don't want to - * propagate that to children */ - sigemptyset(&newmask); - if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) { - Error *local_err = NULL; - error_setg_errno(&local_err, errno, - "cannot unblock signals"); - error_report_err(local_err); - _exit(1); - } - } - return pid; -} - void *qemu_alloc_stack(size_t *sz) { void *ptr, *guardpage; diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 07ade41..16f8a67 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -180,7 +180,7 @@ static int socket_error(void) void qemu_socket_set_block(int fd) { unsigned long opt = 0; - WSAEventSelect(fd, NULL, 0); + qemu_socket_unselect(fd, NULL); ioctlsocket(fd, FIONBIO, &opt); } @@ -283,21 +283,155 @@ char *qemu_get_pid_name(pid_t pid) } -pid_t qemu_fork(Error **errp) +bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents, Error **errp) { - errno = ENOSYS; - error_setg_errno(errp, errno, - "cannot fork child process"); - return -1; + 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; + } + + return true; } +bool qemu_socket_unselect(int sockfd, Error **errp) +{ + return qemu_socket_select(sockfd, NULL, 0, errp); +} + +int qemu_socketpair(int domain, int type, int protocol, int sv[2]) +{ + struct sockaddr_un addr = { + 0, + }; + socklen_t socklen; + int listener = -1; + int client = -1; + int server = -1; + g_autofree char *path = NULL; + int tmpfd; + u_long arg; + int ret = -1; + + g_return_val_if_fail(sv != NULL, -1); + + addr.sun_family = AF_UNIX; + socklen = sizeof(addr); + + tmpfd = g_file_open_tmp(NULL, &path, NULL); + if (tmpfd == -1 || !path) { + errno = EACCES; + goto out; + } + + close(tmpfd); + + if (strlen(path) >= sizeof(addr.sun_path)) { + errno = EINVAL; + goto out; + } + + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + listener = socket(domain, type, protocol); + if (listener == -1) { + goto out; + } + + if (DeleteFile(path) == 0 && GetLastError() != ERROR_FILE_NOT_FOUND) { + errno = EACCES; + goto out; + } + g_clear_pointer(&path, g_free); + + if (bind(listener, (struct sockaddr *)&addr, socklen) == -1) { + goto out; + } + + if (listen(listener, 1) == -1) { + goto out; + } + + client = socket(domain, type, protocol); + if (client == -1) { + goto out; + } + + arg = 1; + if (ioctlsocket(client, FIONBIO, &arg) != NO_ERROR) { + goto out; + } + + if (connect(client, (struct sockaddr *)&addr, socklen) == -1 && + WSAGetLastError() != WSAEWOULDBLOCK) { + goto out; + } + + server = accept(listener, NULL, NULL); + if (server == -1) { + goto out; + } + + arg = 0; + if (ioctlsocket(client, FIONBIO, &arg) != NO_ERROR) { + goto out; + } + + arg = 0; + if (ioctlsocket(client, SIO_AF_UNIX_GETPEERPID, &arg) != NO_ERROR) { + goto out; + } + + if (arg != GetCurrentProcessId()) { + errno = EPERM; + goto out; + } + + sv[0] = server; + server = -1; + sv[1] = client; + client = -1; + ret = 0; + +out: + if (listener != -1) { + close(listener); + } + if (client != -1) { + close(client); + } + if (server != -1) { + close(server); + } + if (path) { + DeleteFile(path); + } + return ret; +} #undef connect 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; @@ -313,7 +447,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(); } @@ -326,7 +466,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(); } @@ -334,15 +480,82 @@ int qemu_bind_wrap(int sockfd, const struct sockaddr *addr, } +#undef close +int qemu_close_wrap(int fd) +{ + int ret; + DWORD flags = 0; + SOCKET s = INVALID_SOCKET; + + if (fd_is_socket(fd)) { + s = _get_osfhandle(fd); + + /* + * 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 (s != INVALID_SOCKET && !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 && (s == INVALID_SOCKET || errno != EBADF)) { + return ret; + } + + if (s != INVALID_SOCKET) { + ret = closesocket(s); + if (ret < 0) { + errno = socket_error(); + } + } + + return ret; +} + + #undef socket int qemu_socket_wrap(int domain, int type, int protocol) { - int ret; - ret = socket(domain, type, protocol); - if (ret < 0) { + SOCKET s; + int fd; + + s = socket(domain, type, protocol); + 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; } @@ -350,12 +563,27 @@ int qemu_socket_wrap(int domain, int type, int protocol) 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; } @@ -363,7 +591,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(); } @@ -375,19 +609,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(); } @@ -400,7 +628,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(); } @@ -413,7 +647,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(); } @@ -426,7 +666,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(); } @@ -439,7 +685,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(); } @@ -451,7 +703,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(); } @@ -464,7 +722,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(); } @@ -476,7 +740,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(); } @@ -489,7 +759,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(); } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 6538859..c06a4dc 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -326,7 +326,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr, * recover from this situation, so we need to recreate the * socket to allow bind attempts for subsequent ports: */ - closesocket(slisten); + close(slisten); slisten = -1; } } @@ -337,7 +337,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr, listen_failed: saved_errno = errno; if (slisten >= 0) { - closesocket(slisten); + close(slisten); } freeaddrinfo(res); errno = saved_errno; @@ -380,7 +380,7 @@ static int inet_connect_addr(const InetSocketAddress *saddr, if (rc < 0) { error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", saddr->host, saddr->port); - closesocket(sock); + close(sock); return -1; } @@ -483,7 +483,7 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) if (ret < 0) { error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); - closesocket(sock); + close(sock); return -1; } } @@ -580,7 +580,7 @@ static int inet_dgram_saddr(InetSocketAddress *sraddr, err: if (sock != -1) { - closesocket(sock); + close(sock); } if (local) { freeaddrinfo(local); @@ -777,7 +777,7 @@ static int vsock_connect_addr(const VsockSocketAddress *vaddr, if (rc < 0) { error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", vaddr->cid, vaddr->port); - closesocket(sock); + close(sock); return -1; } @@ -814,13 +814,13 @@ static int vsock_listen_saddr(VsockSocketAddress *vaddr, if (bind(slisten, (const struct sockaddr *)&svm, sizeof(svm)) != 0) { error_setg_errno(errp, errno, "Failed to bind socket"); - closesocket(slisten); + close(slisten); return -1; } if (listen(slisten, num) != 0) { error_setg_errno(errp, errno, "Failed to listen on socket"); - closesocket(slisten); + close(slisten); return -1; } return slisten; @@ -978,7 +978,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, err: g_free(pathbuf); - closesocket(sock); + close(sock); return -1; } @@ -1041,7 +1041,7 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) return sock; err: - closesocket(sock); + close(sock); return -1; } @@ -1238,7 +1238,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) */ if (listen(fd, num) != 0) { error_setg_errno(errp, errno, "Failed to listen on fd socket"); - closesocket(fd); + close(fd); return -1; } break; |