|
|
6e7d01 |
From 512c7e92808dff66779f7421f1c17a081f18d7e6 Mon Sep 17 00:00:00 2001
|
|
|
6e7d01 |
From: Laurent Vivier <lvivier@redhat.com>
|
|
|
6e7d01 |
Date: Thu, 29 Jul 2021 04:56:46 -0400
|
|
|
6e7d01 |
Subject: [PATCH 13/14] net: check if the file descriptor is valid before using
|
|
|
6e7d01 |
it
|
|
|
6e7d01 |
MIME-Version: 1.0
|
|
|
6e7d01 |
Content-Type: text/plain; charset=UTF-8
|
|
|
6e7d01 |
Content-Transfer-Encoding: 8bit
|
|
|
6e7d01 |
|
|
|
6e7d01 |
RH-Author: Laurent Vivier <lvivier@redhat.com>
|
|
|
6e7d01 |
Message-id: <20210726102337.6359-2-lvivier@redhat.com>
|
|
|
6e7d01 |
Patchwork-id: 101924
|
|
|
6e7d01 |
O-Subject: [RHEL-8.5.0 qemu-kvm PATCH 1/2] net: check if the file descriptor is valid before using it
|
|
|
6e7d01 |
Bugzilla: 1982134
|
|
|
6e7d01 |
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
6e7d01 |
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
|
6e7d01 |
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
|
|
|
6e7d01 |
|
|
|
6e7d01 |
BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1982134
|
|
|
6e7d01 |
BRANCH: rhel-8.5.0
|
|
|
6e7d01 |
UPSTREAM: Merged
|
|
|
6e7d01 |
BREW: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=38380653
|
|
|
6e7d01 |
|
|
|
6e7d01 |
qemu_set_nonblock() checks that the file descriptor can be used and, if
|
|
|
6e7d01 |
not, crashes QEMU. An assert() is used for that. The use of assert() is
|
|
|
6e7d01 |
used to detect programming error and the coredump will allow to debug
|
|
|
6e7d01 |
the problem.
|
|
|
6e7d01 |
|
|
|
6e7d01 |
But in the case of the tap device, this assert() can be triggered by
|
|
|
6e7d01 |
a misconfiguration by the user. At startup, it's not a real problem, but it
|
|
|
6e7d01 |
can also happen during the hot-plug of a new device, and here it's a
|
|
|
6e7d01 |
problem because we can crash a perfectly healthy system.
|
|
|
6e7d01 |
|
|
|
6e7d01 |
For instance:
|
|
|
6e7d01 |
# ip link add link virbr0 name macvtap0 type macvtap mode bridge
|
|
|
6e7d01 |
# ip link set macvtap0 up
|
|
|
6e7d01 |
# TAP=/dev/tap$(ip -o link show macvtap0 | cut -d: -f1)
|
|
|
6e7d01 |
# qemu-system-x86_64 -machine q35 -device pcie-root-port,id=pcie-root-port-0 -monitor stdio 9<> $TAP
|
|
|
6e7d01 |
(qemu) netdev_add type=tap,id=hostnet0,vhost=on,fd=9
|
|
|
6e7d01 |
(qemu) device_add driver=virtio-net-pci,netdev=hostnet0,id=net0,bus=pcie-root-port-0
|
|
|
6e7d01 |
(qemu) device_del net0
|
|
|
6e7d01 |
(qemu) netdev_del hostnet0
|
|
|
6e7d01 |
(qemu) netdev_add type=tap,id=hostnet1,vhost=on,fd=9
|
|
|
6e7d01 |
qemu-system-x86_64: .../util/oslib-posix.c:247: qemu_set_nonblock: Assertion `f != -1' failed.
|
|
|
6e7d01 |
Aborted (core dumped)
|
|
|
6e7d01 |
|
|
|
6e7d01 |
To avoid that, add a function, qemu_try_set_nonblock(), that allows to report the
|
|
|
6e7d01 |
problem without crashing.
|
|
|
6e7d01 |
|
|
|
6e7d01 |
In the same way, we also update the function for vhostfd in net_init_tap_one() and
|
|
|
6e7d01 |
for fd in net_init_socket() (both descriptors are provided by the user and can
|
|
|
6e7d01 |
be wrong).
|
|
|
6e7d01 |
|
|
|
6e7d01 |
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
|
|
|
6e7d01 |
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
|
|
|
6e7d01 |
Signed-off-by: Jason Wang <jasowang@redhat.com>
|
|
|
6e7d01 |
(cherry picked from commit 894022e616016fe81745753f14adfbd680a1c7ee)
|
|
|
6e7d01 |
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
|
|
|
6e7d01 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
6e7d01 |
---
|
|
|
6e7d01 |
include/qemu/sockets.h | 1 +
|
|
|
6e7d01 |
net/socket.c | 9 +++++--
|
|
|
6e7d01 |
net/tap.c | 25 +++++++++++++++---
|
|
|
6e7d01 |
util/oslib-posix.c | 26 +++++++++++++------
|
|
|
6e7d01 |
util/oslib-win32.c | 57 ++++++++++++++++++++++++------------------
|
|
|
6e7d01 |
5 files changed, 79 insertions(+), 39 deletions(-)
|
|
|
6e7d01 |
|
|
|
6e7d01 |
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
|
|
|
6e7d01 |
index 57cd049d6e..7d1f813576 100644
|
|
|
6e7d01 |
--- a/include/qemu/sockets.h
|
|
|
6e7d01 |
+++ b/include/qemu/sockets.h
|
|
|
6e7d01 |
@@ -18,6 +18,7 @@ int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
|
|
|
6e7d01 |
int socket_set_cork(int fd, int v);
|
|
|
6e7d01 |
int socket_set_nodelay(int fd);
|
|
|
6e7d01 |
void qemu_set_block(int fd);
|
|
|
6e7d01 |
+int qemu_try_set_nonblock(int fd);
|
|
|
6e7d01 |
void qemu_set_nonblock(int fd);
|
|
|
6e7d01 |
int socket_set_fast_reuse(int fd);
|
|
|
6e7d01 |
|
|
|
6e7d01 |
diff --git a/net/socket.c b/net/socket.c
|
|
|
6e7d01 |
index c92354049b..2d21fddd9c 100644
|
|
|
6e7d01 |
--- a/net/socket.c
|
|
|
6e7d01 |
+++ b/net/socket.c
|
|
|
6e7d01 |
@@ -725,13 +725,18 @@ int net_init_socket(const Netdev *netdev, const char *name,
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
|
|
|
6e7d01 |
if (sock->has_fd) {
|
|
|
6e7d01 |
- int fd;
|
|
|
6e7d01 |
+ int fd, ret;
|
|
|
6e7d01 |
|
|
|
6e7d01 |
fd = monitor_fd_param(cur_mon, sock->fd, errp);
|
|
|
6e7d01 |
if (fd == -1) {
|
|
|
6e7d01 |
return -1;
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
- qemu_set_nonblock(fd);
|
|
|
6e7d01 |
+ ret = qemu_try_set_nonblock(fd);
|
|
|
6e7d01 |
+ if (ret < 0) {
|
|
|
6e7d01 |
+ error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
|
|
|
6e7d01 |
+ name, fd);
|
|
|
6e7d01 |
+ return -1;
|
|
|
6e7d01 |
+ }
|
|
|
6e7d01 |
if (!net_socket_fd_init(peer, "socket", name, fd, 1, sock->mcast,
|
|
|
6e7d01 |
errp)) {
|
|
|
6e7d01 |
return -1;
|
|
|
6e7d01 |
diff --git a/net/tap.c b/net/tap.c
|
|
|
6e7d01 |
index 6207f61f84..41a20102fd 100644
|
|
|
6e7d01 |
--- a/net/tap.c
|
|
|
6e7d01 |
+++ b/net/tap.c
|
|
|
6e7d01 |
@@ -689,6 +689,8 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
|
|
|
6e7d01 |
if (vhostfdname) {
|
|
|
6e7d01 |
+ int ret;
|
|
|
6e7d01 |
+
|
|
|
6e7d01 |
vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err;;
|
|
|
6e7d01 |
if (vhostfd == -1) {
|
|
|
6e7d01 |
if (tap->has_vhostforce && tap->vhostforce) {
|
|
|
6e7d01 |
@@ -698,7 +700,12 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
return;
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
- qemu_set_nonblock(vhostfd);
|
|
|
6e7d01 |
+ ret = qemu_try_set_nonblock(vhostfd);
|
|
|
6e7d01 |
+ if (ret < 0) {
|
|
|
6e7d01 |
+ error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
|
|
|
6e7d01 |
+ name, fd);
|
|
|
6e7d01 |
+ return;
|
|
|
6e7d01 |
+ }
|
|
|
6e7d01 |
} else {
|
|
|
6e7d01 |
vhostfd = open("/dev/vhost-net", O_RDWR);
|
|
|
6e7d01 |
if (vhostfd < 0) {
|
|
|
6e7d01 |
@@ -766,6 +773,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|
|
6e7d01 |
Error *err = NULL;
|
|
|
6e7d01 |
const char *vhostfdname;
|
|
|
6e7d01 |
char ifname[128];
|
|
|
6e7d01 |
+ int ret = 0;
|
|
|
6e7d01 |
|
|
|
6e7d01 |
assert(netdev->type == NET_CLIENT_DRIVER_TAP);
|
|
|
6e7d01 |
tap = &netdev->u.tap;
|
|
|
6e7d01 |
@@ -795,7 +803,12 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|
|
6e7d01 |
return -1;
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
|
|
|
6e7d01 |
- qemu_set_nonblock(fd);
|
|
|
6e7d01 |
+ ret = qemu_try_set_nonblock(fd);
|
|
|
6e7d01 |
+ if (ret < 0) {
|
|
|
6e7d01 |
+ error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
|
|
|
6e7d01 |
+ name, fd);
|
|
|
6e7d01 |
+ return -1;
|
|
|
6e7d01 |
+ }
|
|
|
6e7d01 |
|
|
|
6e7d01 |
vnet_hdr = tap_probe_vnet_hdr(fd);
|
|
|
6e7d01 |
|
|
|
6e7d01 |
@@ -810,7 +823,6 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|
|
6e7d01 |
char **fds;
|
|
|
6e7d01 |
char **vhost_fds;
|
|
|
6e7d01 |
int nfds = 0, nvhosts = 0;
|
|
|
6e7d01 |
- int ret = 0;
|
|
|
6e7d01 |
|
|
|
6e7d01 |
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
|
|
|
6e7d01 |
tap->has_vnet_hdr || tap->has_helper || tap->has_queues ||
|
|
|
6e7d01 |
@@ -843,7 +855,12 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|
|
6e7d01 |
goto free_fail;
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
|
|
|
6e7d01 |
- qemu_set_nonblock(fd);
|
|
|
6e7d01 |
+ ret = qemu_try_set_nonblock(fd);
|
|
|
6e7d01 |
+ if (ret < 0) {
|
|
|
6e7d01 |
+ error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
|
|
|
6e7d01 |
+ name, fd);
|
|
|
6e7d01 |
+ goto free_fail;
|
|
|
6e7d01 |
+ }
|
|
|
6e7d01 |
|
|
|
6e7d01 |
if (i == 0) {
|
|
|
6e7d01 |
vnet_hdr = tap_probe_vnet_hdr(fd);
|
|
|
6e7d01 |
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
|
|
|
6e7d01 |
index 8f88e4dbe1..db70416dbb 100644
|
|
|
6e7d01 |
--- a/util/oslib-posix.c
|
|
|
6e7d01 |
+++ b/util/oslib-posix.c
|
|
|
6e7d01 |
@@ -240,25 +240,35 @@ void qemu_set_block(int fd)
|
|
|
6e7d01 |
assert(f != -1);
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
|
|
|
6e7d01 |
-void qemu_set_nonblock(int fd)
|
|
|
6e7d01 |
+int qemu_try_set_nonblock(int fd)
|
|
|
6e7d01 |
{
|
|
|
6e7d01 |
int f;
|
|
|
6e7d01 |
f = fcntl(fd, F_GETFL);
|
|
|
6e7d01 |
- assert(f != -1);
|
|
|
6e7d01 |
- f = fcntl(fd, F_SETFL, f | O_NONBLOCK);
|
|
|
6e7d01 |
-#ifdef __OpenBSD__
|
|
|
6e7d01 |
if (f == -1) {
|
|
|
6e7d01 |
+ return -errno;
|
|
|
6e7d01 |
+ }
|
|
|
6e7d01 |
+ if (fcntl(fd, F_SETFL, f | O_NONBLOCK) == -1) {
|
|
|
6e7d01 |
+#ifdef __OpenBSD__
|
|
|
6e7d01 |
/*
|
|
|
6e7d01 |
* Previous to OpenBSD 6.3, fcntl(F_SETFL) is not permitted on
|
|
|
6e7d01 |
* memory devices and sets errno to ENODEV.
|
|
|
6e7d01 |
* It's OK if we fail to set O_NONBLOCK on devices like /dev/null,
|
|
|
6e7d01 |
* because they will never block anyway.
|
|
|
6e7d01 |
*/
|
|
|
6e7d01 |
- assert(errno == ENODEV);
|
|
|
6e7d01 |
- }
|
|
|
6e7d01 |
-#else
|
|
|
6e7d01 |
- assert(f != -1);
|
|
|
6e7d01 |
+ if (errno == ENODEV) {
|
|
|
6e7d01 |
+ return 0;
|
|
|
6e7d01 |
+ }
|
|
|
6e7d01 |
#endif
|
|
|
6e7d01 |
+ return -errno;
|
|
|
6e7d01 |
+ }
|
|
|
6e7d01 |
+ return 0;
|
|
|
6e7d01 |
+}
|
|
|
6e7d01 |
+
|
|
|
6e7d01 |
+void qemu_set_nonblock(int fd)
|
|
|
6e7d01 |
+{
|
|
|
6e7d01 |
+ int f;
|
|
|
6e7d01 |
+ f = qemu_try_set_nonblock(fd);
|
|
|
6e7d01 |
+ assert(f == 0);
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
|
|
|
6e7d01 |
int socket_set_fast_reuse(int fd)
|
|
|
6e7d01 |
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
|
|
|
6e7d01 |
index 3b49d27297..7eedbe5859 100644
|
|
|
6e7d01 |
--- a/util/oslib-win32.c
|
|
|
6e7d01 |
+++ b/util/oslib-win32.c
|
|
|
6e7d01 |
@@ -132,31 +132,6 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
#endif /* CONFIG_LOCALTIME_R */
|
|
|
6e7d01 |
|
|
|
6e7d01 |
-void qemu_set_block(int fd)
|
|
|
6e7d01 |
-{
|
|
|
6e7d01 |
- unsigned long opt = 0;
|
|
|
6e7d01 |
- WSAEventSelect(fd, NULL, 0);
|
|
|
6e7d01 |
- ioctlsocket(fd, FIONBIO, &opt;;
|
|
|
6e7d01 |
-}
|
|
|
6e7d01 |
-
|
|
|
6e7d01 |
-void qemu_set_nonblock(int fd)
|
|
|
6e7d01 |
-{
|
|
|
6e7d01 |
- unsigned long opt = 1;
|
|
|
6e7d01 |
- ioctlsocket(fd, FIONBIO, &opt;;
|
|
|
6e7d01 |
- qemu_fd_register(fd);
|
|
|
6e7d01 |
-}
|
|
|
6e7d01 |
-
|
|
|
6e7d01 |
-int socket_set_fast_reuse(int fd)
|
|
|
6e7d01 |
-{
|
|
|
6e7d01 |
- /* Enabling the reuse of an endpoint that was used by a socket still in
|
|
|
6e7d01 |
- * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows
|
|
|
6e7d01 |
- * fast reuse is the default and SO_REUSEADDR does strange things. So we
|
|
|
6e7d01 |
- * don't have to do anything here. More info can be found at:
|
|
|
6e7d01 |
- * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */
|
|
|
6e7d01 |
- return 0;
|
|
|
6e7d01 |
-}
|
|
|
6e7d01 |
-
|
|
|
6e7d01 |
-
|
|
|
6e7d01 |
static int socket_error(void)
|
|
|
6e7d01 |
{
|
|
|
6e7d01 |
switch (WSAGetLastError()) {
|
|
|
6e7d01 |
@@ -233,6 +208,38 @@ static int socket_error(void)
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
}
|
|
|
6e7d01 |
|
|
|
6e7d01 |
+void qemu_set_block(int fd)
|
|
|
6e7d01 |
+{
|
|
|
6e7d01 |
+ unsigned long opt = 0;
|
|
|
6e7d01 |
+ WSAEventSelect(fd, NULL, 0);
|
|
|
6e7d01 |
+ ioctlsocket(fd, FIONBIO, &opt;;
|
|
|
6e7d01 |
+}
|
|
|
6e7d01 |
+
|
|
|
6e7d01 |
+int qemu_try_set_nonblock(int fd)
|
|
|
6e7d01 |
+{
|
|
|
6e7d01 |
+ unsigned long opt = 1;
|
|
|
6e7d01 |
+ if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) {
|
|
|
6e7d01 |
+ return -socket_error();
|
|
|
6e7d01 |
+ }
|
|
|
6e7d01 |
+ qemu_fd_register(fd);
|
|
|
6e7d01 |
+ return 0;
|
|
|
6e7d01 |
+}
|
|
|
6e7d01 |
+
|
|
|
6e7d01 |
+void qemu_set_nonblock(int fd)
|
|
|
6e7d01 |
+{
|
|
|
6e7d01 |
+ (void)qemu_try_set_nonblock(fd);
|
|
|
6e7d01 |
+}
|
|
|
6e7d01 |
+
|
|
|
6e7d01 |
+int socket_set_fast_reuse(int fd)
|
|
|
6e7d01 |
+{
|
|
|
6e7d01 |
+ /* Enabling the reuse of an endpoint that was used by a socket still in
|
|
|
6e7d01 |
+ * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows
|
|
|
6e7d01 |
+ * fast reuse is the default and SO_REUSEADDR does strange things. So we
|
|
|
6e7d01 |
+ * don't have to do anything here. More info can be found at:
|
|
|
6e7d01 |
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */
|
|
|
6e7d01 |
+ return 0;
|
|
|
6e7d01 |
+}
|
|
|
6e7d01 |
+
|
|
|
6e7d01 |
int inet_aton(const char *cp, struct in_addr *ia)
|
|
|
6e7d01 |
{
|
|
|
6e7d01 |
uint32_t addr = inet_addr(cp);
|
|
|
6e7d01 |
--
|
|
|
6e7d01 |
2.27.0
|
|
|
6e7d01 |
|