diff options
| author | Richard Henderson <richard.henderson@linaro.org> | 2025-09-19 12:20:43 -0700 |
|---|---|---|
| committer | Richard Henderson <richard.henderson@linaro.org> | 2025-09-19 12:20:43 -0700 |
| commit | caf2e8de4ed056acad4fbdb6fe420d8124d38f11 (patch) | |
| tree | 13e664b2b7577724ad773621478e47221dbba5cc | |
| parent | e7c1e8043a69c5a8efa39d4f9d111f7c72c076e6 (diff) | |
| parent | 34523df31962401871ff091a9d0993ac87a9848a (diff) | |
| download | focaccia-qemu-caf2e8de4ed056acad4fbdb6fe420d8124d38f11.tar.gz focaccia-qemu-caf2e8de4ed056acad4fbdb6fe420d8124d38f11.zip | |
Merge tag 'misc-fixes-pull-request' of https://gitlab.com/berrange/qemu into staging
* Update security triage contact address * Check and honour failures to the blocking flag on FDs * Don't touch blocking flags on FDs received during migration # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmjNQuAACgkQvobrtBUQ # T99xaBAAr6zQPii1tjzuzLovF6MIqtldXnmVO/yjcl5NgLWonIRDt2JsxnRxi3es # 9uNDed5+ePNXmUAYd46k81gBEjBWbv465kt5FHAZZV6BRw/PPzkoh+jzGc8NVir8 # 3GZJ2kPr51PxGEl8md2vRthg4bMuhlS5ogCEqAMDYT4f6AVemfnNQ5NttGX353T2 # etxoMhEeMtTBKjMoTBv+SVhhO4nKwZ+6CFhvuGON423EfrGlkNTXyprKTdzpr4i0 # 4KDQLxxoANlmg/1W0PxfrLiBCmGpHweMR44Piv715VYa2YNPRq0G6EC6AFGbHZ51 # N+mKmWNE0CS5rP1TEacSCX4q6If5VxjSLLj+og8LmpIlJ6tiqdrisSqA6bzCJ1f/ # lMsfUsKoMqPhqat9ZGUkYu8REgKP+O+CSGJNftYTsEEY0oKZrAW4fsoN3E9qpfcG # Xy6eSu0TTGDWE6CEe0vkHiQwlVHMtRcWMSPwlsvrgt2TO6k97reT3AoIBK2VfygC # WzMv0P0nBvHFKeIbqmFOk3BEI5+JECgxVRc1WXWbSFLW0PBY/xd7g6ow8uaQsd9e # pzMA1Pwh2EuM4DTlOy+m9zBOhm9YP9An188NLldOne3TFKFYe5QO1DQpvvEGvIGB # +4XpmyOj3g2ycelZZ5XsDJk0LumCCOcbSPSiAvHZyWwLo24EABE= # =rrMd # -----END PGP SIGNATURE----- # gpg: Signature made Fri 19 Sep 2025 04:47:44 AM PDT # gpg: using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [unknown] # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E 8E3F BE86 EBB4 1510 4FDF * tag 'misc-fixes-pull-request' of https://gitlab.com/berrange/qemu: util/vhost-user-server: vu_message_read(): improve error handling chardev: close an fd on failure path chardev: qemu_chr_open_fd(): add errp treewide: use qemu_set_blocking instead of g_unix_set_fd_nonblocking util: drop qemu_socket_set_block() io/channel-socket: rework qio_channel_socket_copy_fds() util: drop qemu_socket_try_set_nonblock() util: drop qemu_socket_set_nonblock() migration: qemu_file_set_blocking(): add errp parameter treewide: handle result of qio_channel_set_blocking() util: add qemu_set_blocking() function char-socket: tcp_chr_recv(): add comment char-socket: tcp_chr_recv(): drop extra _set_(block,cloexec) io/channel: document how qio_channel_readv_full() handles fds migration/qemu-file: don't make incoming fds blocking again MAINTAINERS: list qemu-security@nongnu.org as security contact Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
63 files changed, 369 insertions, 213 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index bd417e96f7..70eb0241d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -85,7 +85,7 @@ Responsible Disclosure, Reporting Security Issues ------------------------------------------------- W: https://wiki.qemu.org/SecurityProcess M: Michael S. Tsirkin <mst@redhat.com> -L: secalert@redhat.com +L: qemu-security@nongnu.org Trivial patches --------------- diff --git a/block/nbd.c b/block/nbd.c index d5a2b21c6d..5d231d5c4e 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -351,7 +351,9 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, return ret; } - qio_channel_set_blocking(s->ioc, false, NULL); + if (!qio_channel_set_blocking(s->ioc, false, errp)) { + return -EINVAL; + } qio_channel_set_follow_coroutine_ctx(s->ioc, true); /* successfully connected */ diff --git a/chardev/char-fd.c b/chardev/char-fd.c index 6f03adf872..4ee286f323 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -206,14 +206,16 @@ int qmp_chardev_open_file_source(char *src, int flags, Error **errp) } /* open a character device to a unix fd */ -void qemu_chr_open_fd(Chardev *chr, - int fd_in, int fd_out) +bool qemu_chr_open_fd(Chardev *chr, + int fd_in, int fd_out, Error **errp) { FDChardev *s = FD_CHARDEV(chr); g_autofree char *name = NULL; - if (fd_out >= 0 && !g_unix_set_fd_nonblocking(fd_out, true, NULL)) { - assert(!"Failed to set FD nonblocking"); + if (fd_out >= 0) { + if (!qemu_set_blocking(fd_out, false, errp)) { + return false; + } } if (fd_out == fd_in && fd_in >= 0) { @@ -221,7 +223,7 @@ void qemu_chr_open_fd(Chardev *chr, name = g_strdup_printf("chardev-file-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name); s->ioc_out = QIO_CHANNEL(object_ref(s->ioc_in)); - return; + return true; } if (fd_in >= 0) { @@ -236,6 +238,8 @@ void qemu_chr_open_fd(Chardev *chr, name = g_strdup_printf("chardev-file-out-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name); } + + return true; } static void char_fd_class_init(ObjectClass *oc, const void *data) diff --git a/chardev/char-file.c b/chardev/char-file.c index a9e8c5e0d7..89e9cb849c 100644 --- a/chardev/char-file.c +++ b/chardev/char-file.c @@ -92,7 +92,11 @@ static void qmp_chardev_open_file(Chardev *chr, } } - qemu_chr_open_fd(chr, in, out); + if (!qemu_chr_open_fd(chr, in, out, errp)) { + qemu_close(out); + qemu_close(in); + return; + } #endif } diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c index 3d1b0ce2d2..e9f3bb8290 100644 --- a/chardev/char-pipe.c +++ b/chardev/char-pipe.c @@ -150,7 +150,14 @@ static void qemu_chr_open_pipe(Chardev *chr, return; } } - qemu_chr_open_fd(chr, fd_in, fd_out); + + if (!qemu_chr_open_fd(chr, fd_in, fd_out, errp)) { + close(fd_in); + if (fd_out != fd_in) { + close(fd_out); + } + return; + } } #endif /* !_WIN32 */ diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 674e9b3f14..b066f01412 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -349,8 +349,8 @@ static void char_pty_open(Chardev *chr, } close(slave_fd); - if (!g_unix_set_fd_nonblocking(master_fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(master_fd, false, errp)) { + close(master_fd); return; } diff --git a/chardev/char-serial.c b/chardev/char-serial.c index 0a68b4b4e0..4c6ca713eb 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -271,13 +271,16 @@ static void qmp_chardev_open_serial(Chardev *chr, if (fd < 0) { return; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); return; } tty_serial_init(fd, 115200, 'N', 8, 1); - qemu_chr_open_fd(chr, fd, fd); + if (!qemu_chr_open_fd(chr, fd, fd, errp)) { + close(fd); + return; + } } #endif /* __linux__ || __sun__ */ diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 1e8313915b..cb4ec78ebe 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -294,7 +294,12 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) } if (msgfds_num) { - /* close and clean read_msgfds */ + /* + * Close and clean previous read_msgfds, they are obsolete at + * this point, regardless result of new call to + * qio_channel_readv_full(). + */ + for (i = 0; i < s->read_msgfds_num; i++) { close(s->read_msgfds[i]); } @@ -307,20 +312,6 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) s->read_msgfds_num = msgfds_num; } - for (i = 0; i < s->read_msgfds_num; i++) { - int fd = s->read_msgfds[i]; - if (fd < 0) { - continue; - } - - /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ - qemu_socket_set_block(fd); - -#ifndef MSG_CMSG_CLOEXEC - qemu_set_cloexec(fd); -#endif - } - if (ret == QIO_CHANNEL_ERR_BLOCK) { errno = EAGAIN; ret = -1; @@ -539,16 +530,24 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) SocketChardev *s = SOCKET_CHARDEV(chr); int size; int saved_errno; + Error *local_err = NULL; if (s->state != TCP_CHARDEV_STATE_CONNECTED) { return 0; } - qio_channel_set_blocking(s->ioc, true, NULL); + if (!qio_channel_set_blocking(s->ioc, true, &local_err)) { + error_report_err(local_err); + return -1; + } size = tcp_chr_recv(chr, (void *) buf, len); saved_errno = errno; if (s->state != TCP_CHARDEV_STATE_DISCONNECTED) { - qio_channel_set_blocking(s->ioc, false, NULL); + if (!qio_channel_set_blocking(s->ioc, false, &local_err)) { + error_report_err(local_err); + /* failed to recover non-blocking state */ + tcp_chr_disconnect(chr); + } } if (size == 0) { /* connection closed */ @@ -893,18 +892,22 @@ static void tcp_chr_set_client_ioc_name(Chardev *chr, static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) { SocketChardev *s = SOCKET_CHARDEV(chr); + Error *local_err = NULL; if (s->state != TCP_CHARDEV_STATE_CONNECTING) { return -1; } + if (!qio_channel_set_blocking(QIO_CHANNEL(sioc), false, &local_err)) { + error_report_err(local_err); + return -1; + } + s->ioc = QIO_CHANNEL(sioc); object_ref(OBJECT(sioc)); s->sioc = sioc; object_ref(OBJECT(sioc)); - qio_channel_set_blocking(s->ioc, false, NULL); - if (s->do_nodelay) { qio_channel_set_delay(s->ioc, false); } diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 48db8d2f30..2568164a10 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -107,18 +107,20 @@ static void qemu_chr_open_stdio(Chardev *chr, old_fd0_flags = fcntl(0, F_GETFL); old_fd1_flags = fcntl(1, F_GETFL); tcgetattr(0, &oldtty); - if (!g_unix_set_fd_nonblocking(0, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(0, false, errp)) { return; } + + if (!qemu_chr_open_fd(chr, 0, 1, errp)) { + return; + } + atexit(term_exit); memset(&act, 0, sizeof(act)); act.sa_handler = term_stdio_handler; sigaction(SIGCONT, &act, NULL); - qemu_chr_open_fd(chr, 0, 1); - stdio_allow_signal = !opts->has_signal || opts->signal; qemu_chr_set_echo_stdio(chr, false); } diff --git a/contrib/ivshmem-server/ivshmem-server.c b/contrib/ivshmem-server/ivshmem-server.c index 2f3c7320a6..13cb828174 100644 --- a/contrib/ivshmem-server/ivshmem-server.c +++ b/contrib/ivshmem-server/ivshmem-server.c @@ -6,6 +6,7 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/host-utils.h" #include "qemu/sockets.h" @@ -135,6 +136,7 @@ ivshmem_server_handle_new_conn(IvshmemServer *server) socklen_t unaddr_len; int newfd; unsigned i; + Error *local_err = NULL; /* accept the incoming connection */ unaddr_len = sizeof(unaddr); @@ -146,9 +148,14 @@ ivshmem_server_handle_new_conn(IvshmemServer *server) return -1; } - qemu_socket_set_nonblock(newfd); IVSHMEM_SERVER_DEBUG(server, "accept()=%d\n", newfd); + if (!qemu_set_blocking(newfd, false, &local_err)) { + error_report_err(local_err); + close(newfd); + return -1; + } + /* allocate new structure for this peer */ peer = g_malloc0(sizeof(*peer)); peer->sock_fd = newfd; diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index ac7e15f6f1..bcdfdf6af7 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -338,7 +338,9 @@ static void hv_syndbg_realize(DeviceState *dev, Error **errp) return; } - qemu_socket_set_nonblock(syndbg->socket); + if (!qemu_set_blocking(syndbg->socket, false, errp)) { + return; + } syndbg->servaddr.sin_port = htons(syndbg->host_port); syndbg->servaddr.sin_family = AF_INET; diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index bbfee9d3b9..9f62532559 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -114,8 +114,7 @@ static void virtio_input_host_realize(DeviceState *dev, Error **errp) error_setg_file_open(errp, errno, vih->evdev); return; } - if (!g_unix_set_fd_nonblocking(vih->fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(vih->fd, false, errp)) { goto err_close; } diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index fe4be6be17..e83e6c6ee9 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -154,7 +154,8 @@ static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, * peer. */ peer->vector[peer->vector_counter].id = peer->vector_counter; - g_unix_set_fd_nonblocking(vector_fd, true, NULL); + /* WARNING: qemu_socket_set_nonblock() return code ignored */ + qemu_set_blocking(vector_fd, false, &error_warn); event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier, vector_fd); diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index d47ae739d6..2748db9286 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c @@ -540,7 +540,12 @@ static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); event_notifier_init_fd(&peer->eventfds[vector], fd); - g_unix_set_fd_nonblocking(fd, true, NULL); /* msix/irqfd poll non block */ + + /* msix/irqfd poll non block */ + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return; + } if (posn == s->vm_id) { setup_interrupt(s, vector, errp); diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index b0165aa2a1..18e0f7a064 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -112,8 +112,12 @@ static void pci_proxy_dev_realize(PCIDevice *device, Error **errp) return; } + if (!qio_channel_set_blocking(dev->ioc, true, errp)) { + object_unref(dev->ioc); + return; + } + qemu_mutex_init(&dev->io_mutex); - qio_channel_set_blocking(dev->ioc, true, NULL); pci_conf[PCI_LATENCY_TIMER] = 0xff; pci_conf[PCI_INTERRUPT_PIN] = 0x01; diff --git a/hw/remote/remote-obj.c b/hw/remote/remote-obj.c index 85882902d7..3402068ab9 100644 --- a/hw/remote/remote-obj.c +++ b/hw/remote/remote-obj.c @@ -107,7 +107,11 @@ static void remote_object_machine_done(Notifier *notifier, void *data) error_report_err(err); return; } - qio_channel_set_blocking(ioc, false, NULL); + if (!qio_channel_set_blocking(ioc, false, &err)) { + error_report_err(err); + object_unref(OBJECT(ioc)); + return; + } o->dev = dev; diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 2c03d49f97..bbd7ec243d 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -886,10 +886,11 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) sioc = qio_channel_socket_new(); ioc = QIO_CHANNEL(sioc); if (qio_channel_socket_connect_sync(sioc, addr, errp) < 0) { - object_unref(OBJECT(ioc)); - return NULL; + goto fail; + } + if (!qio_channel_set_blocking(ioc, false, errp)) { + goto fail; } - qio_channel_set_blocking(ioc, false, NULL); proxy = g_malloc0(sizeof(VFIOUserProxy)); proxy->sockname = g_strdup_printf("unix:%s", sockname); @@ -923,6 +924,10 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) QLIST_INSERT_HEAD(&vfio_user_sockets, proxy, next); return proxy; + +fail: + object_unref(OBJECT(ioc)); + return NULL; } void vfio_user_set_handler(VFIODevice *vbasedev, diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 1e1d6b0d6e..36c9c2e04d 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -2039,7 +2039,10 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) error_setg(errp, "%s: Failed to get ufd", __func__); return -EIO; } - qemu_socket_set_nonblock(ufd); + if (!qemu_set_blocking(ufd, false, errp)) { + close(ufd); + return -EINVAL; + } /* register ufd with userfault thread */ u->postcopy_fd.fd = ufd; diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index 6e4088831f..107d88babe 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -147,9 +147,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) return; } - if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { - error_setg_errno(errp, errno, - "vhost-vsock: unable to set non-blocking mode"); + if (!qemu_set_blocking(vhostfd, false, errp)) { return; } } else { @@ -160,9 +158,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) return; } - if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { - error_setg_errno(errp, errno, - "Failed to set FD nonblocking"); + if (!qemu_set_blocking(vhostfd, false, errp)) { return; } } diff --git a/include/chardev/char-fd.h b/include/chardev/char-fd.h index 9de0e440de..6fe43062ca 100644 --- a/include/chardev/char-fd.h +++ b/include/chardev/char-fd.h @@ -41,7 +41,7 @@ typedef struct FDChardev FDChardev; DECLARE_INSTANCE_CHECKER(FDChardev, FD_CHARDEV, TYPE_CHARDEV_FD) -void qemu_chr_open_fd(Chardev *chr, int fd_in, int fd_out); +bool qemu_chr_open_fd(Chardev *chr, int fd_in, int fd_out, Error **errp); int qmp_chardev_open_file_source(char *src, int flags, Error **errp); #endif /* CHAR_FD_H */ diff --git a/include/io/channel.h b/include/io/channel.h index 234e5db70d..0f25ae0069 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -36,6 +36,7 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass, #define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 #define QIO_CHANNEL_READ_FLAG_RELAXED_EOF 0x2 +#define QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING 0x4 typedef enum QIOChannelFeature QIOChannelFeature; @@ -117,6 +118,15 @@ struct QIOChannelClass { size_t nfds, int flags, Error **errp); + + /* + * The io_readv handler must guarantee that all + * incoming fds are set BLOCKING (unless + * QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING flag is set) and + * CLOEXEC (if available). + * @fds and @nfds are set only on success path. Still, setting + * @fds and @nfds to zero is acceptable on failure path. + */ ssize_t (*io_readv)(QIOChannel *ioc, const struct iovec *iov, size_t niov, @@ -124,6 +134,7 @@ struct QIOChannelClass { size_t *nfds, int flags, Error **errp); + int (*io_close)(QIOChannel *ioc, Error **errp); GSource * (*io_create_watch)(QIOChannel *ioc, @@ -234,6 +245,13 @@ void qio_channel_set_name(QIOChannel *ioc, * was allocated. It is the callers responsibility * to call close() on each file descriptor and to * call g_free() on the array pointer in @fds. + * @fds allocated and set (and @nfds is set too) + * _only_ on success path. Still, @fds and @nfds + * may be set to zero on failure path. + * qio_channel_readv_full() guarantees that all + * incoming fds are set BLOCKING (unless + * QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING flag + * is set) and CLOEXEC (if available). * * It is an error to pass a non-NULL @fds parameter * unless qio_channel_has_feature() returns a true @@ -513,9 +531,9 @@ int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, * return QIO_CHANNEL_ERR_BLOCK if they would otherwise * block on I/O */ -int qio_channel_set_blocking(QIOChannel *ioc, - bool enabled, - Error **errp); +bool qio_channel_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp); /** * qio_channel_set_follow_coroutine_ctx: diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index be3460b32f..1b38cb7e45 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -687,6 +687,7 @@ ssize_t qemu_write_full(int fd, const void *buf, size_t count) G_GNUC_WARN_UNUSED_RESULT; void qemu_set_cloexec(int fd); +bool qemu_set_blocking(int fd, bool block, Error **errp); /* Return a dynamically allocated directory path that is appropriate for storing * local state. diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index c562690d89..be351d85f7 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -46,9 +46,6 @@ ssize_t qemu_send_full(int s, const void *buf, size_t count) G_GNUC_WARN_UNUSED_RESULT; int socket_set_cork(int fd, int v); int socket_set_nodelay(int fd); -void qemu_socket_set_block(int fd); -int qemu_socket_try_set_nonblock(int fd); -void qemu_socket_set_nonblock(int fd); int socket_set_fast_reuse(int fd); #ifdef WIN32 diff --git a/io/channel-command.c b/io/channel-command.c index 8966dd3a2b..8ae9a026b3 100644 --- a/io/channel-command.c +++ b/io/channel-command.c @@ -277,9 +277,12 @@ static int qio_channel_command_set_blocking(QIOChannel *ioc, cioc->blocking = enabled; #else - if ((cioc->writefd >= 0 && !g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL)) || - (cioc->readfd >= 0 && !g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL))) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (cioc->writefd >= 0 && + !qemu_set_blocking(cioc->writefd, enabled, errp)) { + return -1; + } + if (cioc->readfd >= 0 && + !qemu_set_blocking(cioc->readfd, enabled, errp)) { return -1; } #endif diff --git a/io/channel-file.c b/io/channel-file.c index ca3f180cc2..5cef75a67c 100644 --- a/io/channel-file.c +++ b/io/channel-file.c @@ -223,8 +223,7 @@ static int qio_channel_file_set_blocking(QIOChannel *ioc, #else QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); - if (!g_unix_set_fd_nonblocking(fioc->fd, !enabled, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(fioc->fd, enabled, errp)) { return -1; } return 0; diff --git a/io/channel-socket.c b/io/channel-socket.c index 3b7ca924ff..e53d9ac76f 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -472,7 +472,7 @@ static void qio_channel_socket_copy_fds(struct msghdr *msg, *fds = NULL; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { - int fd_size, i; + int fd_size; int gotfds; if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || @@ -490,22 +490,53 @@ static void qio_channel_socket_copy_fds(struct msghdr *msg, gotfds = fd_size / sizeof(int); *fds = g_renew(int, *fds, *nfds + gotfds); memcpy(*fds + *nfds, CMSG_DATA(cmsg), fd_size); + *nfds += gotfds; + } +} - for (i = 0; i < gotfds; i++) { - int fd = (*fds)[*nfds + i]; - if (fd < 0) { - continue; - } +static bool qio_channel_handle_fds(int *fds, size_t nfds, + bool preserve_blocking, Error **errp) +{ + int *end = fds + nfds, *fd; +#ifdef MSG_CMSG_CLOEXEC + if (preserve_blocking) { + /* Nothing to do */ + return true; + } +#endif + + for (fd = fds; fd != end; fd++) { + if (*fd < 0) { + continue; + } + + if (!preserve_blocking) { /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ - qemu_socket_set_block(fd); + if (!qemu_set_blocking(*fd, true, errp)) { + return false; + } + } #ifndef MSG_CMSG_CLOEXEC - qemu_set_cloexec(fd); + qemu_set_cloexec(*fd); #endif + } + + return true; +} + +static void qio_channel_cleanup_fds(int **fds, size_t *nfds) +{ + for (size_t i = 0; i < *nfds; i++) { + if ((*fds)[i] < 0) { + continue; } - *nfds += gotfds; + close((*fds)[i]); } + + g_clear_pointer(fds, g_free); + *nfds = 0; } @@ -556,7 +587,16 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc, } if (fds && nfds) { + bool preserve_blocking = + flags & QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING; + qio_channel_socket_copy_fds(&msg, fds, nfds); + + if (!qio_channel_handle_fds(*fds, *nfds, + preserve_blocking, errp)) { + qio_channel_cleanup_fds(fds, nfds); + return -1; + } } return ret; @@ -820,11 +860,10 @@ qio_channel_socket_set_blocking(QIOChannel *ioc, { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); - if (enabled) { - qemu_socket_set_block(sioc->fd); - } else { - qemu_socket_set_nonblock(sioc->fd); + if (!qemu_set_blocking(sioc->fd, enabled, errp)) { + return -1; } + return 0; } diff --git a/io/channel-tls.c b/io/channel-tls.c index a8248a9216..7135896f79 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -425,7 +425,7 @@ static int qio_channel_tls_set_blocking(QIOChannel *ioc, { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); - return qio_channel_set_blocking(tioc->master, enabled, errp); + return qio_channel_set_blocking(tioc->master, enabled, errp) ? 0 : -1; } static void qio_channel_tls_set_delay(QIOChannel *ioc, diff --git a/io/channel-websock.c b/io/channel-websock.c index 08ddb274f0..0a8c5c4712 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -1184,8 +1184,7 @@ static int qio_channel_websock_set_blocking(QIOChannel *ioc, { QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); - qio_channel_set_blocking(wioc->master, enabled, errp); - return 0; + return qio_channel_set_blocking(wioc->master, enabled, errp) ? 0 : -1; } static void qio_channel_websock_set_delay(QIOChannel *ioc, diff --git a/io/channel.c b/io/channel.c index ebd9322765..852e684938 100644 --- a/io/channel.c +++ b/io/channel.c @@ -359,12 +359,12 @@ int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, } -int qio_channel_set_blocking(QIOChannel *ioc, +bool qio_channel_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); - return klass->io_set_blocking(ioc, enabled, errp); + return klass->io_set_blocking(ioc, enabled, errp) == 0; } diff --git a/migration/colo.c b/migration/colo.c index e0f713c837..cf4d71d9ed 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -859,7 +859,10 @@ static void *colo_process_incoming_thread(void *opaque) * coroutine, and here we are in the COLO incoming thread, so it is ok to * set the fd back to blocked. */ - qemu_file_set_blocking(mis->from_src_file, true); + if (!qemu_file_set_blocking(mis->from_src_file, true, &local_err)) { + error_report_err(local_err); + goto out; + } colo_incoming_start_dirty_log(); diff --git a/migration/migration.c b/migration/migration.c index 10c216d25d..e1ac4d73c2 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -951,7 +951,7 @@ static void migration_incoming_setup(QEMUFile *f) assert(!mis->from_src_file); mis->from_src_file = f; - qemu_file_set_blocking(f, false); + qemu_file_set_blocking(f, false, &error_abort); } void migration_incoming_process(void) @@ -971,7 +971,7 @@ static bool postcopy_try_recover(void) /* This should be set already in migration_incoming_setup() */ assert(mis->from_src_file); /* Postcopy has standalone thread to do vm load */ - qemu_file_set_blocking(mis->from_src_file, true); + qemu_file_set_blocking(mis->from_src_file, true, &error_abort); /* Re-configure the return path */ mis->to_src_file = qemu_file_get_return_path(mis->from_src_file); @@ -4002,7 +4002,9 @@ void migration_connect(MigrationState *s, Error *error_in) } migration_rate_set(rate_limit); - qemu_file_set_blocking(s->to_dst_file, true); + if (!qemu_file_set_blocking(s->to_dst_file, true, &local_err)) { + goto fail; + } /* * Open the return path. For postcopy, it is used exclusively. For diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 45af9a361e..0172172343 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -1909,7 +1909,7 @@ void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file) * The new loading channel has its own threads, so it needs to be * blocked too. It's by default true, just be explicit. */ - qemu_file_set_blocking(file, true); + qemu_file_set_blocking(file, true, &error_abort); mis->postcopy_qemufile_dst = file; qemu_sem_post(&mis->postcopy_qemufile_dst_done); trace_postcopy_preempt_new_channel(); diff --git a/migration/qemu-file.c b/migration/qemu-file.c index b6ac190034..0f4280df21 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -340,7 +340,8 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) do { struct iovec iov = { f->buf + pending, IO_BUF_SIZE - pending }; - len = qio_channel_readv_full(f->ioc, &iov, 1, pfds, pnfd, 0, + len = qio_channel_readv_full(f->ioc, &iov, 1, pfds, pnfd, + QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING, &local_error); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { @@ -887,9 +888,9 @@ void qemu_put_counted_string(QEMUFile *f, const char *str) * both directions, and thus changing the blocking on the main * QEMUFile can also affect the return path. */ -void qemu_file_set_blocking(QEMUFile *f, bool block) +bool qemu_file_set_blocking(QEMUFile *f, bool block, Error **errp) { - qio_channel_set_blocking(f->ioc, block, NULL); + return qio_channel_set_blocking(f->ioc, block, errp); } /* diff --git a/migration/qemu-file.h b/migration/qemu-file.h index f5b9f430e0..c13c967167 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -71,7 +71,7 @@ void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f); int qemu_fflush(QEMUFile *f); -void qemu_file_set_blocking(QEMUFile *f, bool block); +bool qemu_file_set_blocking(QEMUFile *f, bool block, Error **errp); int qemu_file_get_to_fd(QEMUFile *f, int fd, size_t size); void qemu_set_offset(QEMUFile *f, off_t off, int whence); off_t qemu_get_offset(QEMUFile *f); diff --git a/migration/savevm.c b/migration/savevm.c index fabbeb296a..abe0547f9b 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2095,7 +2095,7 @@ static void *postcopy_ram_listen_thread(void *opaque) * Because we're a thread and not a coroutine we can't yield * in qemu_file, and thus we must be blocking now. */ - qemu_file_set_blocking(f, true); + qemu_file_set_blocking(f, true, &error_fatal); /* TODO: sanity check that only postcopiable data will be loaded here */ load_res = qemu_loadvm_state_main(f, mis); @@ -2108,7 +2108,7 @@ static void *postcopy_ram_listen_thread(void *opaque) f = mis->from_src_file; /* And non-blocking again so we don't block in any cleanup */ - qemu_file_set_blocking(f, false); + qemu_file_set_blocking(f, false, &error_fatal); trace_postcopy_ram_listen_thread_exit(); if (load_res < 0) { diff --git a/nbd/server.c b/nbd/server.c index d242be9811..acec0487a8 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1411,7 +1411,9 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) ....options sent, ending in NBD_OPT_EXPORT_NAME or NBD_OPT_GO.... */ - qio_channel_set_blocking(client->ioc, false, NULL); + if (!qio_channel_set_blocking(client->ioc, false, errp)) { + return -EINVAL; + } qio_channel_set_follow_coroutine_ctx(client->ioc, true); trace_nbd_negotiate_begin(); diff --git a/net/dgram.c b/net/dgram.c index 48f653bceb..baa126d514 100644 --- a/net/dgram.c +++ b/net/dgram.c @@ -226,7 +226,10 @@ static int net_dgram_mcast_create(struct sockaddr_in *mcastaddr, } } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + goto fail; + } + return fd; fail: if (fd >= 0) { @@ -284,7 +287,7 @@ static int net_dgram_mcast_init(NetClientState *peer, Error **errp) { NetDgramState *s; - int fd, ret; + int fd; struct sockaddr_in *saddr; if (remote->type != SOCKET_ADDRESS_TYPE_INET) { @@ -332,11 +335,8 @@ static int net_dgram_mcast_init(NetClientState *peer, g_free(saddr); return -1; } - ret = qemu_socket_try_set_nonblock(fd); - if (ret < 0) { + if (!qemu_set_blocking(fd, false, errp)) { g_free(saddr); - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", - name, fd); return -1; } @@ -504,7 +504,11 @@ int net_init_dgram(const Netdev *netdev, const char *name, close(fd); return -1; } - qemu_socket_set_nonblock(fd); + + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } dest_len = sizeof(raddr_in); dest_addr = g_malloc(dest_len); @@ -551,7 +555,10 @@ int net_init_dgram(const Netdev *netdev, const char *name, close(fd); return -1; } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } dest_len = sizeof(raddr_un); dest_addr = g_malloc(dest_len); @@ -562,10 +569,7 @@ int net_init_dgram(const Netdev *netdev, const char *name, if (fd == -1) { return -1; } - ret = qemu_socket_try_set_nonblock(fd); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", - name, fd); + if (!qemu_set_blocking(fd, false, errp)) { return -1; } dest_addr = NULL; diff --git a/net/l2tpv3.c b/net/l2tpv3.c index b5547cb917..cdfc641aa6 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -648,6 +648,9 @@ int net_init_l2tpv3(const Netdev *netdev, error_setg(errp, "could not bind socket err=%i", errno); goto outerr; } + if (!qemu_set_blocking(fd, false, errp)) { + goto outerr; + } freeaddrinfo(result); @@ -709,8 +712,6 @@ int net_init_l2tpv3(const Netdev *netdev, s->vec = g_new(struct iovec, MAX_L2TPV3_IOVCNT); s->header_buf = g_malloc(s->header_size); - qemu_socket_set_nonblock(fd); - s->fd = fd; s->counter = 0; diff --git a/net/socket.c b/net/socket.c index 784dda686f..1ad03fc9d4 100644 --- a/net/socket.c +++ b/net/socket.c @@ -295,7 +295,10 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, } } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + goto fail; + } + return fd; fail: if (fd >= 0) @@ -508,7 +511,10 @@ static int net_socket_listen_init(NetClientState *peer, error_setg_errno(errp, errno, "can't create stream socket"); return -1; } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } socket_set_fast_reuse(fd); @@ -556,7 +562,10 @@ static int net_socket_connect_init(NetClientState *peer, error_setg_errno(errp, errno, "can't create stream socket"); return -1; } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } connected = 0; for(;;) { @@ -671,7 +680,10 @@ static int net_socket_udp_init(NetClientState *peer, close(fd); return -1; } - qemu_socket_set_nonblock(fd); + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } s = net_socket_fd_init_dgram(peer, model, name, fd, 0, NULL, errp); if (!s) { @@ -706,7 +718,7 @@ int net_init_socket(const Netdev *netdev, const char *name, } if (sock->fd) { - int fd, ret, so_type; + int fd, so_type; fd = monitor_fd_param(monitor_cur(), sock->fd, errp); if (fd == -1) { @@ -716,10 +728,7 @@ int net_init_socket(const Netdev *netdev, const char *name, if (so_type < 0) { return -1; } - ret = qemu_socket_try_set_nonblock(fd); - if (ret < 0) { - error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", - name, fd); + if (!qemu_set_blocking(fd, false, errp)) { return -1; } switch (so_type) { diff --git a/net/stream.c b/net/stream.c index d893f02cab..94f823a2a7 100644 --- a/net/stream.c +++ b/net/stream.c @@ -138,7 +138,6 @@ static void net_stream_server_listening(QIOTask *task, gpointer opaque) NetStreamData *d = opaque; QIOChannelSocket *listen_sioc = QIO_CHANNEL_SOCKET(d->listen_ioc); SocketAddress *addr; - int ret; Error *err = NULL; if (qio_task_propagate_error(task, &err)) { @@ -149,13 +148,11 @@ static void net_stream_server_listening(QIOTask *task, gpointer opaque) addr = qio_channel_socket_get_local_address(listen_sioc, NULL); g_assert(addr != NULL); - ret = qemu_socket_try_set_nonblock(listen_sioc->fd); - if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { - qemu_set_info_str(&d->nc, "can't use file descriptor %s (errno %d)", - addr->u.fd.str, -ret); + if (!qemu_set_blocking(listen_sioc->fd, false, &err)) { + qemu_set_info_str(&d->nc, "error: %s", error_get_pretty(err)); + error_free(err); return; } - g_assert(ret == 0); qapi_free_SocketAddress(addr); d->nc.link_down = true; diff --git a/net/stream_data.c b/net/stream_data.c index 5af27e0d1d..03740e9f73 100644 --- a/net/stream_data.c +++ b/net/stream_data.c @@ -12,6 +12,7 @@ #include "net/net.h" #include "io/channel.h" #include "io/net-listener.h" +#include "qemu/sockets.h" #include "stream_data.h" @@ -154,7 +155,6 @@ int net_stream_data_client_connected(QIOTask *task, NetStreamData *d) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(d->ioc); SocketAddress *addr; - int ret; Error *err = NULL; if (qio_task_propagate_error(task, &err)) { @@ -166,14 +166,12 @@ int net_stream_data_client_connected(QIOTask *task, NetStreamData *d) addr = qio_channel_socket_get_remote_address(sioc, NULL); g_assert(addr != NULL); - ret = qemu_socket_try_set_nonblock(sioc->fd); - if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { - qemu_set_info_str(&d->nc, "can't use file descriptor %s (errno %d)", - addr->u.fd.str, -ret); + if (!qemu_set_blocking(sioc->fd, false, &err)) { + qemu_set_info_str(&d->nc, "error: %s", error_get_pretty(err)); + error_free(err); qapi_free_SocketAddress(addr); goto error; } - g_assert(ret == 0); qapi_free_SocketAddress(addr); net_socket_rs_init(&d->rs, net_stream_data_rs_finalize, false); diff --git a/net/tap-bsd.c b/net/tap-bsd.c index b4c84441ba..3f98d0ea82 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -98,7 +98,12 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } } - g_unix_set_fd_nonblocking(fd, true, NULL); + + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } + return fd; } @@ -189,7 +194,10 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, goto error; } - g_unix_set_fd_nonblocking(fd, true, NULL); + if (!qemu_set_blocking(fd, false, errp)) { + goto error; + } + return fd; error: diff --git a/net/tap-linux.c b/net/tap-linux.c index 22ec2f45d2..e832810665 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -124,7 +124,12 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } pstrcpy(ifname, ifname_size, ifr.ifr_name); - g_unix_set_fd_nonblocking(fd, true, NULL); + + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } + return fd; } diff --git a/net/tap-solaris.c b/net/tap-solaris.c index 51b7830bef..af2ebb16f5 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -198,7 +198,12 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, return -1; } } - g_unix_set_fd_nonblocking(fd, true, NULL); + + if (!qemu_set_blocking(fd, false, errp)) { + close(fd); + return -1; + } + return fd; } diff --git a/net/tap.c b/net/tap.c index f7df702f97..f37133e301 100644 --- a/net/tap.c +++ b/net/tap.c @@ -627,8 +627,7 @@ int net_init_bridge(const Netdev *netdev, const char *name, return -1; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(fd, false, errp)) { return -1; } vnet_hdr = tap_probe_vnet_hdr(fd, errp); @@ -729,9 +728,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, error_propagate(errp, err); goto failed; } - if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { - error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", - name, fd); + if (!qemu_set_blocking(vhostfd, false, errp)) { goto failed; } } else { @@ -741,8 +738,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, "tap: open vhost char device failed"); goto failed; } - if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(vhostfd, false, errp)) { goto failed; } } @@ -839,9 +835,7 @@ int net_init_tap(const Netdev *netdev, const char *name, return -1; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { - error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", - name, fd); + if (!qemu_set_blocking(fd, false, errp)) { close(fd); return -1; } @@ -895,10 +889,8 @@ int net_init_tap(const Netdev *netdev, const char *name, goto free_fail; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { + if (!qemu_set_blocking(fd, false, errp)) { ret = -1; - error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", - name, fd); goto free_fail; } @@ -951,8 +943,7 @@ free_fail: return -1; } - if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(fd, false, errp)) { return -1; } vnet_hdr = tap_probe_vnet_hdr(fd, errp); diff --git a/qga/channel-posix.c b/qga/channel-posix.c index 465d688ecb..9ccc8b7bd1 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -28,6 +28,7 @@ static gboolean ga_channel_listen_accept(GIOChannel *channel, GAChannel *c = data; int ret, client_fd; bool accepted = false; + Error *err = NULL; g_assert(channel != NULL); @@ -36,7 +37,11 @@ static gboolean ga_channel_listen_accept(GIOChannel *channel, g_warning("error converting fd to gsocket: %s", strerror(errno)); goto out; } - qemu_socket_set_nonblock(client_fd); + if (!qemu_set_blocking(client_fd, false, &err)) { + g_warning("%s", error_get_pretty(err)); + error_free(err); + goto out; + } ret = ga_channel_client_add(c, client_fd); if (ret) { g_warning("error setting up connection"); diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 12bc086d79..5070f27d75 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -503,9 +503,8 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, /* set fd non-blocking to avoid common use cases (like reading from a * named pipe) from hanging the agent */ - if (!g_unix_set_fd_nonblocking(fileno(fh), true, NULL)) { + if (!qemu_set_blocking(fileno(fh), false, errp)) { fclose(fh); - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); return -1; } diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index b69dd982d6..074b4db472 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -733,8 +733,11 @@ static void coroutine_fn prh_co_entry(void *opaque) uint32_t flags; int r; - qio_channel_set_blocking(QIO_CHANNEL(client->ioc), - false, NULL); + if (!qio_channel_set_blocking(QIO_CHANNEL(client->ioc), + false, &local_err)) { + goto out; + } + qio_channel_set_follow_coroutine_ctx(QIO_CHANNEL(client->ioc), true); /* A very simple negotiation for future extensibility. No features @@ -786,6 +789,7 @@ static void coroutine_fn prh_co_entry(void *opaque) } } +out: if (local_err) { if (verbose == 0) { error_free(local_err); @@ -794,7 +798,6 @@ static void coroutine_fn prh_co_entry(void *opaque) } } -out: object_unref(OBJECT(client->ioc)); g_free(client); } diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c index e239875e3b..e9b13d3e4f 100644 --- a/tests/qtest/fuzz/virtio_net_fuzz.c +++ b/tests/qtest/fuzz/virtio_net_fuzz.c @@ -132,7 +132,7 @@ static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg) { int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds); g_assert_cmpint(ret, !=, -1); - g_unix_set_fd_nonblocking(sockfds[0], true, NULL); + qemu_set_blocking(sockfds[0], false, &error_abort); sockfds_initialized = true; g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sockfds[1]); diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 56472ca709..6ec4ec2d5a 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -330,7 +330,6 @@ static int chr_can_read(void *opaque) static void chr_read(void *opaque, const uint8_t *buf, int size) { - g_autoptr(GError) err = NULL; TestServer *s = opaque; CharBackend *chr = &s->chr; VhostUserMsg msg; @@ -471,8 +470,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * The receive function forces it to be blocking, * so revert it back to non-blocking. */ - g_unix_set_fd_nonblocking(fd, true, &err); - g_assert_no_error(err); + qemu_set_blocking(fd, false, &error_abort); break; case VHOST_USER_SET_LOG_BASE: diff --git a/tests/unit/io-channel-helpers.c b/tests/unit/io-channel-helpers.c index c0799c21c2..22b42d14cd 100644 --- a/tests/unit/io-channel-helpers.c +++ b/tests/unit/io-channel-helpers.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "io-channel-helpers.h" +#include "qapi/error.h" #include "qemu/iov.h" struct QIOChannelTest { @@ -109,8 +110,8 @@ void qio_channel_test_run_threads(QIOChannelTest *test, test->src = src; test->dst = dst; - qio_channel_set_blocking(test->dst, blocking, NULL); - qio_channel_set_blocking(test->src, blocking, NULL); + qio_channel_set_blocking(test->dst, blocking, &error_abort); + qio_channel_set_blocking(test->src, blocking, &error_abort); reader = g_thread_new("reader", test_io_thread_reader, diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c index 37db24f72a..46d2ff101c 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/sockets.h" #include "socket-helpers.h" @@ -88,7 +89,8 @@ static int socket_can_bind_connect(const char *hostname, int family) goto cleanup; } - qemu_socket_set_nonblock(cfd); + qemu_set_blocking(cfd, false, &error_abort); + if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) { if (errno == EINPROGRESS) { check_soerr = true; diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index 554054e934..61311cbe6e 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c @@ -112,8 +112,8 @@ static void test_crypto_tls_session_psk(void) * thread, so we need these non-blocking to avoid deadlock * of ourselves */ - qemu_socket_set_nonblock(channel[0]); - qemu_socket_set_nonblock(channel[1]); + qemu_set_blocking(channel[0], false, &error_abort); + qemu_set_blocking(channel[1], false, &error_abort); clientCreds = test_tls_creds_psk_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, @@ -264,8 +264,8 @@ static void test_crypto_tls_session_x509(const void *opaque) * thread, so we need these non-blocking to avoid deadlock * of ourselves */ - qemu_socket_set_nonblock(channel[0]); - qemu_socket_set_nonblock(channel[1]); + qemu_set_blocking(channel[0], false, &error_abort); + qemu_set_blocking(channel[1], false, &error_abort); #define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" #define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" diff --git a/tests/unit/test-io-channel-tls.c b/tests/unit/test-io-channel-tls.c index e036ac5df4..6f282ad45d 100644 --- a/tests/unit/test-io-channel-tls.c +++ b/tests/unit/test-io-channel-tls.c @@ -184,8 +184,8 @@ static void test_io_channel_tls(const void *opaque) * thread, so we need these non-blocking to avoid deadlock * of ourselves */ - qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL); - qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL); + qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, &error_abort); + qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, &error_abort); /* Now the real part of the test, setup the sessions */ clientChanTLS = qio_channel_tls_new_client( diff --git a/tests/unit/test-iov.c b/tests/unit/test-iov.c index 75bc3be005..63e2b1583c 100644 --- a/tests/unit/test-iov.c +++ b/tests/unit/test-iov.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/iov.h" #include "qemu/sockets.h" @@ -186,7 +187,7 @@ static void test_io(void) close(sv[0]); FD_SET(sv[1], &fds); - g_unix_set_fd_nonblocking(sv[1], true, NULL); + qemu_set_blocking(sv[1], false, &error_abort); r = g_test_rand_int_range(sz / 2, sz); setsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &r, sizeof(r)); @@ -222,7 +223,7 @@ static void test_io(void) close(sv[1]); FD_SET(sv[0], &fds); - g_unix_set_fd_nonblocking(sv[0], true, NULL); + qemu_set_blocking(sv[0], false, &error_abort); r = g_test_rand_int_range(sz / 2, sz); setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &r, sizeof(r)); usleep(500000); diff --git a/tools/i386/qemu-vmsr-helper.c b/tools/i386/qemu-vmsr-helper.c index 5f19a48cbd..6c0f4fe870 100644 --- a/tools/i386/qemu-vmsr-helper.c +++ b/tools/i386/qemu-vmsr-helper.c @@ -213,8 +213,10 @@ static void coroutine_fn vh_co_entry(void *opaque) uint64_t vmsr; int r; - qio_channel_set_blocking(QIO_CHANNEL(client->ioc), - false, NULL); + if (!qio_channel_set_blocking(QIO_CHANNEL(client->ioc), + false, &local_err)) { + goto out; + } qio_channel_set_follow_coroutine_ctx(QIO_CHANNEL(client->ioc), true); diff --git a/ui/input-linux.c b/ui/input-linux.c index 92e1a1aa64..44d0c15a9b 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -316,8 +316,7 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) error_setg_file_open(errp, errno, il->evdev); return; } - if (!g_unix_set_fd_nonblocking(il->fd, true, NULL)) { - error_setg_errno(errp, errno, "Failed to set FD nonblocking"); + if (!qemu_set_blocking(il->fd, false, errp)) { return; } diff --git a/ui/vnc.c b/ui/vnc.c index 9054fc8125..77c823bf2e 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3337,7 +3337,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, VNC_DEBUG("New client on socket %p\n", vs->sioc); update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); - qio_channel_set_blocking(vs->ioc, false, NULL); + qio_channel_set_blocking(vs->ioc, false, &error_abort); if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); } diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c index 76420c5b56..83fdbb96bb 100644 --- a/util/event_notifier-posix.c +++ b/util/event_notifier-posix.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" @@ -36,6 +37,7 @@ int event_notifier_init(EventNotifier *e, int active) { int fds[2]; int ret; + Error *local_err = NULL; #ifdef CONFIG_EVENTFD ret = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); @@ -52,11 +54,11 @@ int event_notifier_init(EventNotifier *e, int active) if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { return -errno; } - if (!g_unix_set_fd_nonblocking(fds[0], true, NULL)) { + if (!qemu_set_blocking(fds[0], false, &local_err)) { ret = -errno; goto fail; } - if (!g_unix_set_fd_nonblocking(fds[1], true, NULL)) { + if (!qemu_set_blocking(fds[1], false, &local_err)) { ret = -errno; goto fail; } @@ -70,6 +72,7 @@ int event_notifier_init(EventNotifier *e, int active) return 0; fail: + error_report_err(local_err); close(fds[0]); close(fds[1]); return ret; diff --git a/util/main-loop.c b/util/main-loop.c index 51aeb2432e..b8ddda8f5e 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -114,7 +114,10 @@ static int qemu_signal_init(Error **errp) return -errno; } - g_unix_set_fd_nonblocking(sigfd, true, NULL); + if (!qemu_set_blocking(sigfd, false, errp)) { + close(sigfd); + return -EINVAL; + } qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd); diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 4ff577e5de..14cf94ac03 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -250,21 +250,19 @@ void qemu_anon_ram_free(void *ptr, size_t size) #endif } -void qemu_socket_set_block(int fd) +bool qemu_set_blocking(int fd, bool block, Error **errp) { - g_unix_set_fd_nonblocking(fd, false, NULL); -} + g_autoptr(GError) err = NULL; -int qemu_socket_try_set_nonblock(int fd) -{ - return g_unix_set_fd_nonblocking(fd, true, NULL) ? 0 : -errno; -} + if (!g_unix_set_fd_nonblocking(fd, !block, &err)) { + error_setg_errno(errp, errno, + "Can't set file descriptor %d %s: %s", fd, + block ? "blocking" : "non-blocking", + err->message); + return false; + } -void qemu_socket_set_nonblock(int fd) -{ - int f; - f = qemu_socket_try_set_nonblock(fd); - assert(f == 0); + return true; } int socket_set_fast_reuse(int fd) diff --git a/util/oslib-win32.c b/util/oslib-win32.c index b7351634ec..b9ce2f96ee 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -177,25 +177,22 @@ static int socket_error(void) } } -void qemu_socket_set_block(int fd) +bool qemu_set_blocking(int fd, bool block, Error **errp) { - unsigned long opt = 0; - qemu_socket_unselect(fd, NULL); - ioctlsocket(fd, FIONBIO, &opt); -} + unsigned long opt = block ? 0 : 1; + + if (block) { + qemu_socket_unselect(fd, NULL); + } -int qemu_socket_try_set_nonblock(int fd) -{ - unsigned long opt = 1; if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) { - return -socket_error(); + error_setg_errno(errp, socket_error(), + "Can't set file descriptor %d %s", fd, + block ? "blocking" : "non-blocking"); + return false; } - return 0; -} -void qemu_socket_set_nonblock(int fd) -{ - (void)qemu_socket_try_set_nonblock(fd); + return true; } int socket_set_fast_reuse(int fd) diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index b19229074a..1dbe409f82 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -62,7 +62,7 @@ static void vmsg_close_fds(VhostUserMsg *vmsg) } } -static void vmsg_unblock_fds(VhostUserMsg *vmsg) +static bool vmsg_unblock_fds(VhostUserMsg *vmsg, Error **errp) { int i; @@ -74,12 +74,16 @@ static void vmsg_unblock_fds(VhostUserMsg *vmsg) */ if (vmsg->request == VHOST_USER_ADD_MEM_REG || vmsg->request == VHOST_USER_SET_MEM_TABLE) { - return; + return true; } for (i = 0; i < vmsg->fd_num; i++) { - qemu_socket_set_nonblock(vmsg->fds[i]); + if (!qemu_set_blocking(vmsg->fds[i], false, errp)) { + return false; + } } + + return true; } static void panic_cb(VuDev *vu_dev, const char *buf) @@ -122,7 +126,6 @@ vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) vmsg->fd_num = 0; if (!ioc) { - error_report_err(local_err); goto fail; } @@ -176,7 +179,10 @@ vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) } while (read_bytes != VHOST_USER_HDR_SIZE); /* qio_channel_readv_full will make socket fds blocking, unblock them */ - vmsg_unblock_fds(vmsg); + if (!vmsg_unblock_fds(vmsg, &local_err)) { + error_report_err(local_err); + goto fail; + } if (vmsg->size > sizeof(vmsg->payload)) { error_report("Error: too big message request: %d, " "size: vmsg->size: %u, " @@ -303,7 +309,8 @@ set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_fd_watch->fd = fd; vu_fd_watch->cb = cb; - qemu_socket_set_nonblock(fd); + /* TODO: handle error more gracefully than aborting */ + qemu_set_blocking(fd, false, &error_abort); aio_set_fd_handler(server->ctx, fd, kick_handler, NULL, NULL, NULL, vu_fd_watch); vu_fd_watch->vu_dev = vu_dev; @@ -336,6 +343,7 @@ static void vu_accept(QIONetListener *listener, QIOChannelSocket *sioc, gpointer opaque) { VuServer *server = opaque; + Error *local_err = NULL; if (server->sioc) { warn_report("Only one vhost-user client is allowed to " @@ -368,7 +376,11 @@ static void vu_accept(QIONetListener *listener, QIOChannelSocket *sioc, object_ref(OBJECT(server->ioc)); /* TODO vu_message_write() spins if non-blocking! */ - qio_channel_set_blocking(server->ioc, false, NULL); + if (!qio_channel_set_blocking(server->ioc, false, &local_err)) { + error_report_err(local_err); + vu_deinit(&server->vu_dev); + return; + } qio_channel_set_follow_coroutine_ctx(server->ioc, true); |