git: 0bd5ef6b4363 - main - virtual_oss(8): Properly cleanup cuse(3)

From: Christos Margiolis <christos_at_FreeBSD.org>
Date: Thu, 11 Jun 2026 09:31:09 UTC
The branch main has been updated by christos:

URL: https://cgit.FreeBSD.org/src/commit/?id=0bd5ef6b43633a3cf77495a087a9376b2b3b11c9

commit 0bd5ef6b43633a3cf77495a087a9376b2b3b11c9
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2026-05-29 11:32:42 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2026-06-11 09:30:22 +0000

    virtual_oss(8): Properly cleanup cuse(3)
    
    virtual_oss(8) does not currently keep track of the cuse(3) it creates,
    nor does it destroy any of them on exit, except for the control device.
    This is harmless if virtual_oss(8) is killed after all audio streams
    have been shut down, but if it's killed during I/O, the process hangs
    and/or goes into uninterruptible sleep state.
    
    To fix this, have pointers to all cuse(3) devices, and explicitly
    destroy them on exit. Also make sure we don't leak memory in
    dup_profile().
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Reviewed by:    jrm
    Pull-Request:   https://ron-dev.freebsd.org/FreeBSD/src/pulls/41
---
 usr.sbin/virtual_oss/virtual_oss/int.h  |  2 ++
 usr.sbin/virtual_oss/virtual_oss/main.c | 39 +++++++++++++++++++++++++++++----
 2 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/usr.sbin/virtual_oss/virtual_oss/int.h b/usr.sbin/virtual_oss/virtual_oss/int.h
index 974f1a51f573..7b7cabd62743 100644
--- a/usr.sbin/virtual_oss/virtual_oss/int.h
+++ b/usr.sbin/virtual_oss/virtual_oss/int.h
@@ -122,6 +122,8 @@ struct virtual_profile {
 	vclient_head_t head;
 	char oss_name[VMAX_STRING];
 	char wav_name[VMAX_STRING];
+	struct cuse_dev *oss_dev;
+	struct cuse_dev *wav_dev;
 	uint32_t rx_filter_size;
 	uint32_t tx_filter_size;
 	double *rx_filter_data[VMAX_CHAN];
diff --git a/usr.sbin/virtual_oss/virtual_oss/main.c b/usr.sbin/virtual_oss/virtual_oss/main.c
index 1d24be89f3da..9eafd5549a5c 100644
--- a/usr.sbin/virtual_oss/virtual_oss/main.c
+++ b/usr.sbin/virtual_oss/virtual_oss/main.c
@@ -1858,6 +1858,15 @@ done:
 	nvlist_destroy(nvl);
 }
 
+static void
+cleanup_profile(vprofile_t *pvp)
+{
+	if (pvp->oss_dev != NULL)
+		cuse_dev_destroy(pvp->oss_dev);
+	if (pvp->wav_dev != NULL)
+		cuse_dev_destroy(pvp->wav_dev);
+}
+
 static const char *
 dup_profile(vprofile_t *pvp, int *pamp, int pol, int rx_mute,
     int tx_mute, int synchronized, int is_client)
@@ -1865,6 +1874,7 @@ dup_profile(vprofile_t *pvp, int *pamp, int pol, int rx_mute,
 	vprofile_t *ptr;
 	struct cuse_dev *pdev;
 	struct group *gr;
+	const char *errstr;
 	gid_t gid;
 	int x, perm;
 
@@ -1937,9 +1947,10 @@ dup_profile(vprofile_t *pvp, int *pamp, int pol, int rx_mute,
 		pdev = cuse_dev_create(&vclient_oss_methods, ptr, NULL,
 		    0, gid, perm, ptr->oss_name);
 		if (pdev == NULL) {
-			free(ptr);
-			return ("Could not create CUSE DSP device");
+			errstr = "Could not create CUSE DSP device";
+			goto err;
 		}
+		ptr->oss_dev = pdev;
 
 		/* register to sndstat */
 		ptr->fd_sta = open("/dev/sndstat", O_WRONLY);
@@ -1954,9 +1965,10 @@ dup_profile(vprofile_t *pvp, int *pamp, int pol, int rx_mute,
 		pdev = cuse_dev_create(&vclient_wav_methods, ptr, NULL,
 		    0, gid, perm, ptr->wav_name);
 		if (pdev == NULL) {
-			free(ptr);
-			return ("Could not create CUSE WAV device");
+			errstr = "Could not create CUSE WAV device";
+			goto err;
 		}
+		ptr->wav_dev = pdev;
 	}
 
 	atomic_lock();
@@ -1991,6 +2003,13 @@ dup_profile(vprofile_t *pvp, int *pamp, int pol, int rx_mute,
 	init_compressor(pvp);
 
 	return (voss_httpd_start(ptr));
+
+err:
+	cleanup_profile(ptr);
+	free(ptr);
+
+	return (errstr);
+
 }
 
 static void
@@ -2560,6 +2579,7 @@ main(int argc, char **argv)
 	const char *ptrerr;
 	struct sigaction sa;
 	struct cuse_dev *pdev = NULL;
+	struct virtual_profile *pvp;
 
 	TAILQ_INIT(&virtual_profile_client_head);
 	TAILQ_INIT(&virtual_profile_loopback_head);
@@ -2645,8 +2665,19 @@ main(int argc, char **argv)
 
 	destroy_threads();
 
+	/* Destroy CUSE devices */
+
 	if (voss_ctl_device[0] != 0)
 		cuse_dev_destroy(pdev);
 
+	TAILQ_FOREACH(pvp, &virtual_profile_client_head, entry) {
+		cleanup_profile(pvp);
+	}
+	TAILQ_FOREACH(pvp, &virtual_profile_loopback_head, entry) {
+		cleanup_profile(pvp);
+	}
+
+	cuse_uninit();
+
 	return (0);
 }