From: Yehor Malikov <[email protected]>

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 properly stop the dispatch thread
before freeing resources:
- Set destroy flag to signal thread exit
- Wait for thread completion via rte_thread_join()
- Close epoll fd and free memory only after thread exits

Add RTE_FINI destructor to ensure fdset cleanup runs before EAL
cleanup frees hugepages.

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

Signed-off-by: Yehor Malikov <[email protected]>
---
 .mailmap           |  1 +
 lib/vhost/fd_man.c | 33 +++++++++++++++++++++++++++++++++
 lib/vhost/fd_man.h |  1 +
 lib/vhost/socket.c |  8 ++++++++
 4 files changed, 43 insertions(+)

diff --git a/.mailmap b/.mailmap
index 34a99f93a1..6fb87ca810 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1800,6 +1800,7 @@ Yaron Illouz <[email protected]>
 Yaroslav Brustinov <[email protected]>
 Yash Sharma <[email protected]>
 Yasufumi Ogawa <[email protected]> <[email protected]>
+Yehor Malikov <[email protected]>
 Yelena Krivosheev <[email protected]>
 Yerden Zhumabekov <[email protected]> <[email protected]>
 Yevgeny Kliteynik <[email protected]>
diff --git a/lib/vhost/fd_man.c b/lib/vhost/fd_man.c
index f9147edee7..4c759d44a4 100644
--- a/lib/vhost/fd_man.c
+++ b/lib/vhost/fd_man.c
@@ -149,6 +149,39 @@ fdset_init(const char *name)
        return NULL;
 }
 
+void
+fdset_deinit(struct fdset *pfdset)
+{
+       unsigned int val;
+       int i;
+
+       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);
+
+       /* Close epoll fd */
+       close(pfdset->epfd);
+
+       /* Remove from global fdsets list */
+       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 */
+       rte_free(pfdset);
+}
+
 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..e953dd1849 100644
--- a/lib/vhost/socket.c
+++ b/lib/vhost/socket.c
@@ -1209,3 +1209,11 @@ rte_vhost_driver_start(const char *path)
        else
                return vhost_user_start_client(vsocket);
 }
+
+RTE_FINI(vhost_user_fdset_fini)
+{
+       if (vhost_user.fdset != NULL) {
+               fdset_deinit(vhost_user.fdset);
+               vhost_user.fdset = NULL;
+       }
+}
-- 
2.52.0

Reply via email to