patch 'vhost: fix use-after-free race during cleanup' has been queued to stable release 25.11.1
Kevin Traynor
ktraynor at redhat.com
Thu Mar 19 11:02:34 CET 2026
Hi,
FYI, your patch has been queued to stable release 25.11.1
Note it hasn't been pushed to http://dpdk.org/browse/dpdk-stable yet.
It will be pushed if I get no objections before 03/23/26. So please
shout if anyone has objections.
Also note that after the patch there's a diff of the upstream commit vs the
patch applied to the branch. This will indicate if there was any rebasing
needed to apply to the stable branch. If there were code changes for rebasing
(ie: not only metadata diffs), please double check that the rebase was
correctly done.
Queued patches are on a temporary branch at:
https://github.com/kevintraynor/dpdk-stable
This queued commit can be viewed at:
https://github.com/kevintraynor/dpdk-stable/commit/0b3392426d8ac2b984c9f44aca794a92467de9a9
Thanks.
Kevin
---
>From 0b3392426d8ac2b984c9f44aca794a92467de9a9 Mon Sep 17 00:00:00 2001
From: Shani Peretz <shperetz at nvidia.com>
Date: Thu, 29 Jan 2026 10:34:34 +0200
Subject: [PATCH] vhost: fix use-after-free race during cleanup
[ upstream commit 73ea44370d35f7683cb5e59a721f191aec724eb6 ]
This commit fixes a use-after-free that causes the application to crash
on shutdown (detected by ASAN).
The vhost library uses a background event dispatch thread that monitors
fds with epoll. It runs in an infinite loop, waiting for I/O events
and calling callbacks when they occur.
During cleanup, a race condition existed:
Main Thread: Event Dispatch Thread:
1. Remove fds from fdset while (1) {
2. Close file descriptors epoll_wait() [gets interrupted]
3. rte_eal_cleanup() [continues loop]
4. Unmap hugepages Accesses fdset... CRASH
}
There was no explicit cleanup of the fdset structure.
The fdset structure is allocated with rte_zmalloc() and the memory would
only be reclaimed at application shutdown when rte_eal_cleanup() is called,
which invokes rte_eal_memory_detach() to unmap all the hugepage memory.
Meanwhile, the event dispatch thread could still be running and accessing
the fdset.
The code had a `destroy` flag that the event dispatch thread checked,
but it was never set during cleanup, and the code never waited for
the thread to actually exit before freeing memory.
To fix this, the commit implements fdset_destroy() that sets the destroy
flag with mutex protection, waits for thread termination, and cleans up
all resources including the fdset memory.
Update socket.c to call fdset_destroy() when the last vhost-user socket
is unregistered.
Fixes: 0e38b42bf61c ("vhost: manage FD with epoll")
Signed-off-by: Shani Peretz <shperetz at nvidia.com>
Reviewed-by: Maxime Coquelin <maxime.coquelin at redhat.com>
---
lib/vhost/fd_man.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
lib/vhost/fd_man.h | 1 +
lib/vhost/socket.c | 7 +++++++
3 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/lib/vhost/fd_man.c b/lib/vhost/fd_man.c
index f9147edee7..b4597dec75 100644
--- a/lib/vhost/fd_man.c
+++ b/lib/vhost/fd_man.c
@@ -388,5 +388,8 @@ fdset_event_dispatch(void *arg)
}
- if (pfdset->destroy)
+ pthread_mutex_lock(&pfdset->fd_mutex);
+ bool should_destroy = pfdset->destroy;
+ pthread_mutex_unlock(&pfdset->fd_mutex);
+ if (should_destroy)
break;
}
@@ -394,2 +397,42 @@ fdset_event_dispatch(void *arg)
return 0;
}
+
+/**
+ * Destroy the fdset and stop its event dispatch thread.
+ */
+void
+fdset_destroy(struct fdset *pfdset)
+{
+ uint32_t val;
+ int i;
+
+ if (pfdset == NULL)
+ return;
+
+ /* Signal the event dispatch thread to stop */
+ pthread_mutex_lock(&pfdset->fd_mutex);
+ pfdset->destroy = true;
+ pthread_mutex_unlock(&pfdset->fd_mutex);
+
+ /* Wait for the event dispatch thread to finish */
+ rte_thread_join(pfdset->tid, &val);
+
+ /* Close the epoll file descriptor */
+ close(pfdset->epfd);
+
+ /* Destroy the mutex */
+ pthread_mutex_destroy(&pfdset->fd_mutex);
+
+ /* Remove from global registry */
+ pthread_mutex_lock(&fdsets_mutex);
+ for (i = 0; i < MAX_FDSETS; i++) {
+ if (fdsets[i] == pfdset) {
+ fdsets[i] = NULL;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&fdsets_mutex);
+
+ /* Free the fdset structure */
+ rte_free(pfdset);
+}
diff --git a/lib/vhost/fd_man.h b/lib/vhost/fd_man.h
index eadcc6fb42..ed2109f3c8 100644
--- a/lib/vhost/fd_man.h
+++ b/lib/vhost/fd_man.h
@@ -22,4 +22,5 @@ int fdset_add(struct fdset *pfdset, int fd,
void fdset_del(struct fdset *pfdset, int fd);
int fdset_try_del(struct fdset *pfdset, int fd);
+void fdset_destroy(struct fdset *pfdset);
#endif
diff --git a/lib/vhost/socket.c b/lib/vhost/socket.c
index ae95e7e6b0..eb01231478 100644
--- a/lib/vhost/socket.c
+++ b/lib/vhost/socket.c
@@ -1142,4 +1142,11 @@ again:
vhost_user.vsockets[i] = vhost_user.vsockets[count];
vhost_user.vsockets[count] = NULL;
+
+ /* Check if we need to destroy the vhost fdset */
+ if (vhost_user.vsocket_cnt == 0 && vhost_user.fdset != NULL) {
+ fdset_destroy(vhost_user.fdset);
+ vhost_user.fdset = NULL;
+ }
+
pthread_mutex_unlock(&vhost_user.mutex);
return 0;
--
2.53.0
---
Diff of the applied patch vs upstream commit (please double-check if non-empty:
---
--- - 2026-03-19 10:01:08.187677228 +0000
+++ 0036-vhost-fix-use-after-free-race-during-cleanup.patch 2026-03-19 10:01:07.104331261 +0000
@@ -1 +1 @@
-From 73ea44370d35f7683cb5e59a721f191aec724eb6 Mon Sep 17 00:00:00 2001
+From 0b3392426d8ac2b984c9f44aca794a92467de9a9 Mon Sep 17 00:00:00 2001
@@ -5,0 +6,2 @@
+[ upstream commit 73ea44370d35f7683cb5e59a721f191aec724eb6 ]
+
@@ -41 +42,0 @@
-Cc: stable at dpdk.org
More information about the stable
mailing list