aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2026-08-24 15:20:32 +0000
committerMark Johnston <markj@FreeBSD.org>2026-08-25 15:58:57 +0000
commite6c1c92155578688b9999f7a88cfe5d438c401f1 (patch)
tree53798f71cecff38bb07629860f0b6ba9208d98c8
parent5eaecdb275d34eae25de5c13a170e5c36c1cd11e (diff)
dsp: Fix a potential use-after-free in dsp_oss_syncstart()
This function has a loop where it attempts to lock all channels in a group. If doing so would block, it releases all locks, sleeps for a bit, and tries again. However, once the syncgroup lock is dropped, nothing prevents the syncgroup structure from being freed. Fix the inner loop: after waking up, break out of it unconditionally and start everything again. I think the old code was also buggy and not well-exercised: after waking up we'd continue to try and continue locking channels. Then we'd try again from the beginning and fail to lock the channels we had already locked. Approved by: so Security: FreeBSD-SA-26:58.sound Security: CVE-2026-58091 Reported by: Hazley Samsudin of GovTech CSG Reviewed by: christos Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D58912
-rw-r--r--sys/dev/sound/pcm/channel.h9
-rw-r--r--sys/dev/sound/pcm/dsp.c17
2 files changed, 11 insertions, 15 deletions
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index 9ad21d219001..059dc23993cd 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -57,10 +57,11 @@ struct pcmchan_syncmember;
extern struct mtx snd_pcm_syncgroups_mtx;
extern SLIST_HEAD(pcm_synclist, pcmchan_syncgroup) snd_pcm_syncgroups;
-#define PCM_SG_LOCK() mtx_lock(&snd_pcm_syncgroups_mtx)
-#define PCM_SG_TRYLOCK() mtx_trylock(&snd_pcm_syncgroups_mtx)
-#define PCM_SG_UNLOCK() mtx_unlock(&snd_pcm_syncgroups_mtx)
-#define PCM_SG_LOCKASSERT(arg) mtx_assert(&snd_pcm_syncgroups_mtx, arg)
+#define PCM_SG_LOCKPTR() (&snd_pcm_syncgroups_mtx)
+#define PCM_SG_LOCK() mtx_lock(PCM_SG_LOCKPTR())
+#define PCM_SG_TRYLOCK() mtx_trylock(PCM_SG_LOCKPTR())
+#define PCM_SG_UNLOCK() mtx_unlock(PCM_SG_LOCKPTR())
+#define PCM_SG_LOCKASSERT(arg) mtx_assert(PCM_SG_LOCKPTR(), arg)
/**
* @brief Specifies an audio device sync group
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 4d69f176225c..96cd794c1fed 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -2720,14 +2720,11 @@ dsp_oss_syncstart(int sg_id)
struct pcmchan_syncmember *sm, *sm_tmp;
struct pcmchan_syncgroup *sg;
struct pcm_channel *c;
- int ret, needlocks;
+ int ret;
- /* Get the synclists lock */
PCM_SG_LOCK();
-
do {
ret = 0;
- needlocks = 0;
/* Search for syncgroup by ID */
SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
@@ -2764,16 +2761,14 @@ dsp_oss_syncstart(int sg_id)
}
/** @todo Is PRIBIO correct/ */
- ret = msleep(sm, &snd_pcm_syncgroups_mtx,
+ ret = msleep(sm, PCM_SG_LOCKPTR(),
PRIBIO | PCATCH, "pcmsg", timo);
- if (ret == EINTR || ret == ERESTART)
- break;
-
- needlocks = 1;
- ret = 0; /* Assumes ret == EAGAIN... */
+ if (ret == EAGAIN)
+ ret = 0;
+ break;
}
}
- } while (needlocks && ret == 0);
+ } while (ret == 0 && sm != NULL);
/* Proceed only if no errors encountered. */
if (ret == 0) {