[PATCH v3] vhost: fix use-after-free in fdset during shutdown

Yehor Malikov malikovyehor at gmail.com
Wed Feb 4 22:32:39 CET 2026


From: Yehor Malikov <Yehor.Malikov at solidigm.com>

The fdset_event_dispatch thread runs in a loop checking the destroy
flag after each epoll_wait iteration. During process exit,
rte_eal_cleanup() frees hugepages memory while the fdset thread is
still running, causing use-after-free when accessing the fdset
structure.

Add fdset_deinit() function to stop the dispatch thread by setting
the destroy flag and waiting for thread completion. Resource cleanup
(epoll fd, fdsets array, memory) is intentionally skipped since the
function is only called during process exit when the OS will reclaim
all resources anyway, avoiding potential deadlocks from mutex
operations in destructor context.

Use symmetric RTE_INIT/RTE_FINI constructors and destructors for both
vhost-user and VDUSE fdsets to ensure proper initialization at library
load and cleanup before EAL teardown.

Fixes: e68a6feaa3b3 ("vhost: improve fdset initialization")

Signed-off-by: Yehor Malikov <Yehor.Malikov at solidigm.com>
---
 .mailmap           |  1 +
 lib/vhost/fd_man.c | 16 ++++++++++++++++
 lib/vhost/fd_man.h |  1 +
 lib/vhost/socket.c | 24 +++++++++++++++++++-----
 lib/vhost/vduse.c  | 23 ++++++++++++++++++-----
 5 files changed, 55 insertions(+), 10 deletions(-)

diff --git a/.mailmap b/.mailmap
index 34a99f93a1..6fb87ca810 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1800,6 +1800,7 @@ Yaron Illouz <yaroni at radcom.com>
 Yaroslav Brustinov <ybrustin at cisco.com>
 Yash Sharma <ysharma at marvell.com>
 Yasufumi Ogawa <ogawa.yasufumi at lab.ntt.co.jp> <yasufum.o at gmail.com>
+Yehor Malikov <Yehor.Malikov at solidigm.com>
 Yelena Krivosheev <yelena at marvell.com>
 Yerden Zhumabekov <e_zhumabekov at sts.kz> <yerden.zhumabekov at sts.kz>
 Yevgeny Kliteynik <kliteyn at nvidia.com>
diff --git a/lib/vhost/fd_man.c b/lib/vhost/fd_man.c
index f9147edee7..9790c8a586 100644
--- a/lib/vhost/fd_man.c
+++ b/lib/vhost/fd_man.c
@@ -149,6 +149,22 @@ fdset_init(const char *name)
 	return NULL;
 }
 
+void
+fdset_deinit(struct fdset *pfdset)
+{
+	unsigned int val;
+
+	if (pfdset == NULL)
+		return;
+
+	/* Signal the dispatch thread to stop */
+	pfdset->destroy = true;
+
+	/* Wait for the dispatch thread to exit */
+	if (rte_thread_join(pfdset->tid, &val) != 0)
+		VHOST_FDMAN_LOG(ERR, "Failed to join %s event dispatch thread", pfdset->name);
+}
+
 static int
 fdset_insert_entry(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat)
 {
diff --git a/lib/vhost/fd_man.h b/lib/vhost/fd_man.h
index eadcc6fb42..c9e51badaa 100644
--- a/lib/vhost/fd_man.h
+++ b/lib/vhost/fd_man.h
@@ -15,6 +15,7 @@ struct fdset;
 typedef void (*fd_cb)(int fd, void *dat, int *close);
 
 struct fdset *fdset_init(const char *name);
+void fdset_deinit(struct fdset *pfdset);
 
 int fdset_add(struct fdset *pfdset, int fd,
 	fd_cb rcb, fd_cb wcb, void *dat);
diff --git a/lib/vhost/socket.c b/lib/vhost/socket.c
index 9b4f332f94..93d129774f 100644
--- a/lib/vhost/socket.c
+++ b/lib/vhost/socket.c
@@ -76,6 +76,8 @@ struct vhost_user_connection {
 };
 
 #define MAX_VHOST_SOCKET 1024
+#define VHOST_USER_FDSET_NAME "vhost-evt"
+
 struct vhost_user {
 	struct vhost_user_socket *vsockets[MAX_VHOST_SOCKET];
 	struct fdset *fdset;
@@ -1197,11 +1199,8 @@ rte_vhost_driver_start(const char *path)
 		return vduse_device_create(path, vsocket->net_compliant_ol_flags);
 
 	if (vhost_user.fdset == NULL) {
-		vhost_user.fdset = fdset_init("vhost-evt");
-		if (vhost_user.fdset == NULL) {
-			VHOST_CONFIG_LOG(path, ERR, "failed to init Vhost-user fdset");
-			return -1;
-		}
+		VHOST_CONFIG_LOG(path, ERR, "Vhost-user fdset not initialized");
+		return -1;
 	}
 
 	if (vsocket->is_server)
@@ -1209,3 +1208,18 @@ rte_vhost_driver_start(const char *path)
 	else
 		return vhost_user_start_client(vsocket);
 }
+
+RTE_INIT(vhost_user_fdset_init)
+{
+	vhost_user.fdset = fdset_init(VHOST_USER_FDSET_NAME);
+	if (vhost_user.fdset == NULL)
+		VHOST_CONFIG_LOG(VHOST_USER_FDSET_NAME, ERR, "failed to init Vhost-user fdset");
+}
+
+RTE_FINI(vhost_user_fdset_fini)
+{
+	if (vhost_user.fdset != NULL) {
+		fdset_deinit(vhost_user.fdset);
+		vhost_user.fdset = NULL;
+	}
+}
diff --git a/lib/vhost/vduse.c b/lib/vhost/vduse.c
index 9de7f04a4f..e770bd61d4 100644
--- a/lib/vhost/vduse.c
+++ b/lib/vhost/vduse.c
@@ -27,6 +27,7 @@
 
 #define VHOST_VDUSE_API_VERSION 0
 #define VDUSE_CTRL_PATH "/dev/vduse/control"
+#define VDUSE_FDSET_NAME "vduse-evt"
 
 struct vduse {
 	struct fdset *fdset;
@@ -627,11 +628,8 @@ vduse_device_create(const char *path, bool compliant_ol_flags)
 	bool reconnect = false;
 
 	if (vduse.fdset == NULL) {
-		vduse.fdset = fdset_init("vduse-evt");
-		if (vduse.fdset == NULL) {
-			VHOST_CONFIG_LOG(path, ERR, "failed to init VDUSE fdset");
-			return -1;
-		}
+		VHOST_CONFIG_LOG(path, ERR, "VDUSE fdset not initialized");
+		return -1;
 	}
 
 	control_fd = open(VDUSE_CTRL_PATH, O_RDWR);
@@ -878,3 +876,18 @@ vduse_device_destroy(const char *path)
 
 	return 0;
 }
+
+RTE_INIT(vduse_fdset_init)
+{
+	vduse.fdset = fdset_init(VDUSE_FDSET_NAME);
+	if (vduse.fdset == NULL)
+		VHOST_CONFIG_LOG(VDUSE_FDSET_NAME, ERR, "failed to init VDUSE fdset");
+}
+
+RTE_FINI(vduse_fdset_fini)
+{
+	if (vduse.fdset != NULL) {
+		fdset_deinit(vduse.fdset);
+		vduse.fdset = NULL;
+	}
+}
-- 
2.52.0



More information about the dev mailing list