public inbox for dev@dpdk.org
 help / color / mirror / Atom feed
From: Shani Peretz <shperetz@nvidia.com>
To: <dev@dpdk.org>
Cc: <maxime.coquelin@redhat.com>, Shani Peretz <shperetz@nvidia.com>,
	<stable@dpdk.org>, Chenbo Xia <chenbox@nvidia.com>,
	David Marchand <david.marchand@redhat.com>
Subject: [PATCH v3] vhost: fix use-after-free race during cleanup
Date: Thu, 29 Jan 2026 10:34:34 +0200	[thread overview]
Message-ID: <20260129083435.5054-1-shperetz@nvidia.com> (raw)
In-Reply-To: <20251104080931.8102-1-shperetz@nvidia.com>

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")
Cc: stable@dpdk.org

Signed-off-by: Shani Peretz <shperetz@nvidia.com>

-----------------
v3: 
removed vduse implementation from this fix

---
 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
@@ -387,9 +387,52 @@ 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;
 	}
 
 	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
@@ -21,5 +21,6 @@ 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
@@ -1141,6 +1141,13 @@ rte_vhost_driver_unregister(const char *path)
 		count = --vhost_user.vsocket_cnt;
 		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.43.0


  parent reply	other threads:[~2026-01-29  8:35 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-04  8:09 [PATCH] vhost: fix use-after-free race during cleanup Shani Peretz
2025-11-04  9:32 ` fengchengwen
2025-11-04 14:31   ` Maxime Coquelin
2025-11-09 12:25   ` Shani Peretz
2025-11-09 14:26 ` [PATCH v2] " Shani Peretz
2026-01-20 13:55   ` Maxime Coquelin
2026-01-29  7:28     ` Shani Peretz
2026-01-29  8:34 ` Shani Peretz [this message]
2026-01-29 17:21   ` [PATCH v3] " Stephen Hemminger
2026-03-05 10:47   ` Maxime Coquelin
2026-03-05 13:51     ` Maxime Coquelin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260129083435.5054-1-shperetz@nvidia.com \
    --to=shperetz@nvidia.com \
    --cc=chenbox@nvidia.com \
    --cc=david.marchand@redhat.com \
    --cc=dev@dpdk.org \
    --cc=maxime.coquelin@redhat.com \
    --cc=stable@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox