git: 013af29c1e17 - stable/15 - virtual_oss(8): Properly cleanup cuse(3)

From: Christos Margiolis <christos_at_FreeBSD.org>
Date: Thu, 18 Jun 2026 19:33:42 UTC
The branch stable/15 has been updated by christos:

URL: https://cgit.FreeBSD.org/src/commit/?id=013af29c1e1727ae4c8cae8167d4ad55a1c1d778

commit 013af29c1e1727ae4c8cae8167d4ad55a1c1d778
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2026-05-29 11:32:42 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2026-06-18 19:33:35 +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
    
    (cherry picked from commit 0bd5ef6b43633a3cf77495a087a9376b2b3b11c9)
---
 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 69a943832074..dbe35dbefd58 100644
--- a/usr.sbin/virtual_oss/virtual_oss/int.h
+++ b/usr.sbin/virtual_oss/virtual_oss/int.h
@@ -120,6 +120,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 b66d17d940af..a6cf691e7fbb 100644
--- a/usr.sbin/virtual_oss/virtual_oss/main.c
+++ b/usr.sbin/virtual_oss/virtual_oss/main.c
@@ -1856,6 +1856,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)
@@ -1863,6 +1872,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;
 
@@ -1935,9 +1945,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);
@@ -1952,9 +1963,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();
@@ -1989,6 +2001,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
@@ -2558,6 +2577,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);
@@ -2643,8 +2663,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);
 }