Linux kernel -stable discussions
 help / color / mirror / Atom feed
* FAILED: patch "[PATCH] ALSA: emux: Fix UAF of soundfont zones at note-on" failed to apply to 6.1-stable tree
@ 2026-11-07 10:52 Sasha Levin
  2026-11-10 23:19 ` [PATCH 6.1.y 1/5] ALSA: emux: prune unused parameter from snd_soundfont_load_guspatch() Sasha Levin
  0 siblings, 1 reply; 6+ messages in thread
From: Sasha Levin @ 2026-11-07 10:52 UTC (permalink / raw)
  To: tiwai, sashiko-bot; +Cc: stable

The patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x 34e3e04b8a1731a773797542f0781e99ce7cd755
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '20261107055202-failed-6.1-34e3e04b8a17@kernel.org' --subject-prefix 'PATCH 6.1.y' 'HEAD^..'

Thanks,
Sasha

------------------ original commit in Linus's tree ------------------

From 34e3e04b8a1731a773797542f0781e99ce7cd755 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Tue, 20 Oct 2026 12:16:20 +0200
Subject: [PATCH] ALSA: emux: Fix UAF of soundfont zones at note-on

snd_emux_note_on() looks up the matching zones via
snd_soundfont_search_zone(), which takes sflist->lock only during the
lookup and returns the raw zone pointers after releasing it.  The
caller then stores them in vp->zone without any protection.
Meanwhile, a concurrent SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES ioctl may
unlink and free those zones in snd_soundfont_remove_unlocked(); this
leads to a use-after-free in snd_emux_note_on(), and later at
accessing the cached vp->zone, e.g. in snd_emux_send_effect().

Fix it by holding sflist->lock in snd_emux_note_on() over the whole
zone lookup and voice setup, and let snd_soundfont_search_zone()
require the caller to hold the lock, instead.
snd_soundfont_lock_preset() takes sflist->lock for setting
presets_locked, so the removal path now syncs with the note-on finish.

Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/bug/linux-697212bb-a434-4ff5-8778-25a84f5dad89
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261020101624.205638-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/synth/emux/emux_synth.c | 3 +++
 sound/synth/emux/soundfont.c  | 9 ++++++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
index 6982e1317da5e..2cbf12364a1e5 100644
--- a/sound/synth/emux/emux_synth.c
+++ b/sound/synth/emux/emux_synth.c
@@ -60,6 +60,9 @@ snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
 		return;
 
 	key = note; /* remember the original note */
+
+	/* hold sflist->lock for protecting against concurrent free */
+	guard(spinlock_irqsave)(&emu->sflist->lock);
 	nvoices = get_zone(emu, port, &note, vel, chan, table);
 	if (! nvoices)
 		return;
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index c172b53b17a4f..039357d96e07a 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -1225,6 +1225,9 @@ delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp)
  * The found zones are stored on 'table' array.  max_layers defines
  * the maximum number of elements in this array.
  * This function returns the number of found zones.  0 if not found.
+ *
+ * The caller must hold sflist->lock, and keep holding it as long as
+ * the returned zones are accessed.
  */
 int
 snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
@@ -1234,11 +1237,11 @@ snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
 {
 	int nvoices;
 
-	/* this function is supposed to be called atomically,
-	 * so we check the lock.  if it's busy, just returns 0 to
+	lockdep_assert_held(&sflist->lock);
+
+	/* if the presets are being modified, just returns 0 to
 	 * tell the caller the busy state
 	 */
-	guard(spinlock_irqsave)(&sflist->lock);
 	if (sflist->presets_locked)
 		return 0;
 	nvoices = search_zones(sflist, notep, vel, preset, bank,
-- 
2.53.0





^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 6.1.y 1/5] ALSA: emux: prune unused parameter from snd_soundfont_load_guspatch()
  2026-11-07 10:52 FAILED: patch "[PATCH] ALSA: emux: Fix UAF of soundfont zones at note-on" failed to apply to 6.1-stable tree Sasha Levin
@ 2026-11-10 23:19 ` Sasha Levin
  2026-11-10 23:19   ` [PATCH 6.1.y 2/5] ALSA: emux: fix validation of snd_emux.num_ports Sasha Levin
                     ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-10 23:19 UTC (permalink / raw)
  To: stable; +Cc: Oswald Buddenhagen, Takashi Iwai, Sasha Levin

From: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>

[ Upstream commit 3f3e0dfc83d586fe9204936fccae771754a9dbc2 ]

The `client` parameter was not used, so eliminate it from the call
chain.

Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Message-ID: <20240406064830.1029573-3-oswald.buddenhagen@gmx.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Stable-dep-of: 34e3e04b8a17 ("ALSA: emux: Fix UAF of soundfont zones at note-on")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/sound/soundfont.h     | 2 +-
 sound/synth/emux/emux_hwdep.c | 3 +--
 sound/synth/emux/emux_oss.c   | 3 +--
 sound/synth/emux/soundfont.c  | 7 +++----
 4 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/include/sound/soundfont.h b/include/sound/soundfont.h
index e445688a4f4f9..98ed98d89d6de 100644
--- a/include/sound/soundfont.h
+++ b/include/sound/soundfont.h
@@ -89,7 +89,7 @@ struct snd_sf_list {
 int snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
 		       long count, int client);
 int snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
-				long count, int client);
+				long count);
 int snd_soundfont_close_check(struct snd_sf_list *sflist, int client);
 
 struct snd_sf_list *snd_sf_new(struct snd_sf_callback *callback,
diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c
index 81719bfb8ed73..fd8f978cde1c6 100644
--- a/sound/synth/emux/emux_hwdep.c
+++ b/sound/synth/emux/emux_hwdep.c
@@ -27,8 +27,7 @@ snd_emux_hwdep_load_patch(struct snd_emux *emu, void __user *arg)
 
 	if (patch.key == GUS_PATCH)
 		return snd_soundfont_load_guspatch(emu->sflist, arg,
-						   patch.len + sizeof(patch),
-						   TMP_CLIENT_ID);
+						   patch.len + sizeof(patch));
 
 	if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
 	    patch.type <= SNDRV_SFNT_PROBE_DATA) {
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index d8d32671f7038..04df46b269d37 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -205,8 +205,7 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
 		return -ENXIO;
 
 	if (format == GUS_PATCH)
-		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count,
-						 SF_CLIENT_NO(p->chset.port));
+		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count);
 	else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
 		struct soundfont_patch_info patch;
 		if (count < (int)sizeof(patch))
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index eed47e4830248..77144fe221cf2 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -944,8 +944,7 @@ int snd_sf_vol_table[128] = {
 
 /* load GUS patch */
 static int
-load_guspatch(struct snd_sf_list *sflist, const char __user *data,
-	      long count, int client)
+load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count)
 {
 	struct patch_info patch;
 	struct snd_soundfont *sf;
@@ -1127,11 +1126,11 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
 /* load GUS patch */
 int
 snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
-			    long count, int client)
+			    long count)
 {
 	int rc;
 	lock_preset(sflist);
-	rc = load_guspatch(sflist, data, count, client);
+	rc = load_guspatch(sflist, data, count);
 	unlock_preset(sflist);
 	return rc;
 }
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 6.1.y 2/5] ALSA: emux: fix validation of snd_emux.num_ports
  2026-11-10 23:19 ` [PATCH 6.1.y 1/5] ALSA: emux: prune unused parameter from snd_soundfont_load_guspatch() Sasha Levin
@ 2026-11-10 23:19   ` Sasha Levin
  2026-11-10 23:19   ` [PATCH 6.1.y 3/5] ALSA: emux: Use standard print API Sasha Levin
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-10 23:19 UTC (permalink / raw)
  To: stable; +Cc: Oswald Buddenhagen, Takashi Iwai, Sasha Levin

From: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>

[ Upstream commit 19061f35b3eaf4925960be44d870244b99df8d1d ]

Both bounds had off-by-one errors.

Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Message-ID: <20240406064830.1029573-4-oswald.buddenhagen@gmx.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Stable-dep-of: 34e3e04b8a17 ("ALSA: emux: Fix UAF of soundfont zones at note-on")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 sound/synth/emux/emux_seq.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c
index b227c7e0bc2ac..1adaa75df2f6a 100644
--- a/sound/synth/emux/emux_seq.c
+++ b/sound/synth/emux/emux_seq.c
@@ -65,11 +65,11 @@ snd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index)
 		return -ENODEV;
 	}
 
-	if (emu->num_ports < 0) {
+	if (emu->num_ports <= 0) {
 		snd_printk(KERN_WARNING "seqports must be greater than zero\n");
 		emu->num_ports = 1;
-	} else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) {
-		snd_printk(KERN_WARNING "too many ports."
+	} else if (emu->num_ports > SNDRV_EMUX_MAX_PORTS) {
+		snd_printk(KERN_WARNING "too many ports. "
 			   "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS);
 		emu->num_ports = SNDRV_EMUX_MAX_PORTS;
 	}
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 6.1.y 3/5] ALSA: emux: Use standard print API
  2026-11-10 23:19 ` [PATCH 6.1.y 1/5] ALSA: emux: prune unused parameter from snd_soundfont_load_guspatch() Sasha Levin
  2026-11-10 23:19   ` [PATCH 6.1.y 2/5] ALSA: emux: fix validation of snd_emux.num_ports Sasha Levin
@ 2026-11-10 23:19   ` Sasha Levin
  2026-11-10 23:19   ` [PATCH 6.1.y 4/5] ALSA: synth: Use guard() for spin locks Sasha Levin
  2026-11-10 23:19   ` [PATCH 6.1.y 5/5] ALSA: emux: Fix UAF of soundfont zones at note-on Sasha Levin
  3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-10 23:19 UTC (permalink / raw)
  To: stable; +Cc: Takashi Iwai, Jaroslav Kysela, Sasha Levin

From: Takashi Iwai <tiwai@suse.de>

[ Upstream commit fea1510719dd2a33e0ffa1eae6118c5437f7071d ]

Use the standard print API with dev_*() instead of the old house-baked
one.  It gives better information and allows dynamically control of
debug prints.

Some functions are changed to receive snd_card object for calling
dev_*() functions, too.

Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20240807133452.9424-44-tiwai@suse.de
Stable-dep-of: 34e3e04b8a17 ("ALSA: emux: Fix UAF of soundfont zones at note-on")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/sound/soundfont.h     |  6 ++--
 sound/synth/emux/emux_hwdep.c |  6 ++--
 sound/synth/emux/emux_oss.c   | 11 +++---
 sound/synth/emux/emux_seq.c   | 13 ++++---
 sound/synth/emux/emux_synth.c | 12 +++----
 sound/synth/emux/soundfont.c  | 67 +++++++++++++++++++----------------
 6 files changed, 63 insertions(+), 52 deletions(-)

diff --git a/include/sound/soundfont.h b/include/sound/soundfont.h
index 98ed98d89d6de..8a40cc15f66db 100644
--- a/include/sound/soundfont.h
+++ b/include/sound/soundfont.h
@@ -86,9 +86,11 @@ struct snd_sf_list {
 };
 
 /* Prototypes for soundfont.c */
-int snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
+int snd_soundfont_load(struct snd_card *card,
+		       struct snd_sf_list *sflist, const void __user *data,
 		       long count, int client);
-int snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
+int snd_soundfont_load_guspatch(struct snd_card *card,
+				struct snd_sf_list *sflist, const char __user *data,
 				long count);
 int snd_soundfont_close_check(struct snd_sf_list *sflist, int client);
 
diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c
index fd8f978cde1c6..9e98c4c73fd1e 100644
--- a/sound/synth/emux/emux_hwdep.c
+++ b/sound/synth/emux/emux_hwdep.c
@@ -26,12 +26,14 @@ snd_emux_hwdep_load_patch(struct snd_emux *emu, void __user *arg)
 		return -EFAULT;
 
 	if (patch.key == GUS_PATCH)
-		return snd_soundfont_load_guspatch(emu->sflist, arg,
+		return snd_soundfont_load_guspatch(emu->card, emu->sflist, arg,
 						   patch.len + sizeof(patch));
 
 	if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
 	    patch.type <= SNDRV_SFNT_PROBE_DATA) {
-		err = snd_soundfont_load(emu->sflist, arg, patch.len + sizeof(patch), TMP_CLIENT_ID);
+		err = snd_soundfont_load(emu->card, emu->sflist, arg,
+					 patch.len + sizeof(patch),
+					 TMP_CLIENT_ID);
 		if (err < 0)
 			return err;
 	} else {
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index 04df46b269d37..5f98d5201ba25 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -115,7 +115,7 @@ snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
 	p = snd_emux_create_port(emu, tmpname, 32,
 				 1, &callback);
 	if (p == NULL) {
-		snd_printk(KERN_ERR "can't create port\n");
+		dev_err(emu->card->dev, "can't create port\n");
 		snd_emux_dec_count(emu);
 		return -ENOMEM;
 	}
@@ -205,7 +205,7 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
 		return -ENXIO;
 
 	if (format == GUS_PATCH)
-		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count);
+		rc = snd_soundfont_load_guspatch(emu->card, emu->sflist, buf, count);
 	else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
 		struct soundfont_patch_info patch;
 		if (count < (int)sizeof(patch))
@@ -214,10 +214,13 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
 			return -EFAULT;
 		if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
 		    patch.type <= SNDRV_SFNT_PROBE_DATA)
-			rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port));
+			rc = snd_soundfont_load(emu->card, emu->sflist, buf,
+						count,
+						SF_CLIENT_NO(p->chset.port));
 		else {
 			if (emu->ops.load_fx)
-				rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count);
+				rc = emu->ops.load_fx(emu, patch.type,
+						      patch.optarg, buf, count);
 			else
 				rc = -EINVAL;
 		}
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c
index 1adaa75df2f6a..9daced0e6c59d 100644
--- a/sound/synth/emux/emux_seq.c
+++ b/sound/synth/emux/emux_seq.c
@@ -61,16 +61,17 @@ snd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index)
 	emu->client = snd_seq_create_kernel_client(card, index,
 						   "%s WaveTable", emu->name);
 	if (emu->client < 0) {
-		snd_printk(KERN_ERR "can't create client\n");
+		dev_err(card->dev, "can't create client\n");
 		return -ENODEV;
 	}
 
 	if (emu->num_ports <= 0) {
-		snd_printk(KERN_WARNING "seqports must be greater than zero\n");
+		dev_warn(card->dev, "seqports must be greater than zero\n");
 		emu->num_ports = 1;
 	} else if (emu->num_ports > SNDRV_EMUX_MAX_PORTS) {
-		snd_printk(KERN_WARNING "too many ports. "
-			   "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS);
+		dev_warn(card->dev,
+			 "too many ports. limited max. ports %d\n",
+			 SNDRV_EMUX_MAX_PORTS);
 		emu->num_ports = SNDRV_EMUX_MAX_PORTS;
 	}
 
@@ -87,7 +88,7 @@ snd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index)
 		p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS,
 					 0, &pinfo);
 		if (!p) {
-			snd_printk(KERN_ERR "can't create port\n");
+			dev_err(card->dev, "can't create port\n");
 			return -ENOMEM;
 		}
 
@@ -376,12 +377,10 @@ int snd_emux_init_virmidi(struct snd_emux *emu, struct snd_card *card)
 			goto __error;
 		}
 		emu->vmidi[i] = rmidi;
-		/* snd_printk(KERN_DEBUG "virmidi %d ok\n", i); */
 	}
 	return 0;
 
 __error:
-	/* snd_printk(KERN_DEBUG "error init..\n"); */
 	snd_emux_delete_virmidi(emu);
 	return -ENOMEM;
 }
diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
index a5385efcedb6c..ebf672cdbc31b 100644
--- a/sound/synth/emux/emux_synth.c
+++ b/sound/synth/emux/emux_synth.c
@@ -941,9 +941,9 @@ void snd_emux_lock_voice(struct snd_emux *emu, int voice)
 	if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF)
 		emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED;
 	else
-		snd_printk(KERN_WARNING
-			   "invalid voice for lock %d (state = %x)\n",
-			   voice, emu->voices[voice].state);
+		dev_warn(emu->card->dev,
+			 "invalid voice for lock %d (state = %x)\n",
+			 voice, emu->voices[voice].state);
 	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
@@ -959,9 +959,9 @@ void snd_emux_unlock_voice(struct snd_emux *emu, int voice)
 	if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED)
 		emu->voices[voice].state = SNDRV_EMUX_ST_OFF;
 	else
-		snd_printk(KERN_WARNING
-			   "invalid voice for unlock %d (state = %x)\n",
-			   voice, emu->voices[voice].state);
+		dev_warn(emu->card->dev,
+			 "invalid voice for unlock %d (state = %x)\n",
+			 voice, emu->voices[voice].state);
 	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 77144fe221cf2..03f70cceb4a86 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -38,7 +38,8 @@ static struct snd_sf_sample *sf_sample_new(struct snd_sf_list *sflist,
 static void sf_sample_delete(struct snd_sf_list *sflist,
 			     struct snd_soundfont *sf, struct snd_sf_sample *sp);
 static int load_map(struct snd_sf_list *sflist, const void __user *data, int count);
-static int load_info(struct snd_sf_list *sflist, const void __user *data, long count);
+static int load_info(struct snd_card *card, struct snd_sf_list *sflist,
+		     const void __user *data, long count);
 static int remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf,
 		       int bank, int instr);
 static void init_voice_info(struct soundfont_voice_info *avp);
@@ -113,7 +114,8 @@ snd_soundfont_close_check(struct snd_sf_list *sflist, int client)
  * it wants to do with it.
  */
 int
-snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
+snd_soundfont_load(struct snd_card *card,
+		   struct snd_sf_list *sflist, const void __user *data,
 		   long count, int client)
 {
 	struct soundfont_patch_info patch;
@@ -121,7 +123,7 @@ snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
 	int  rc;
 
 	if (count < (long)sizeof(patch)) {
-		snd_printk(KERN_ERR "patch record too small %ld\n", count);
+		dev_err(card->dev, "patch record too small %ld\n", count);
 		return -EINVAL;
 	}
 	if (copy_from_user(&patch, data, sizeof(patch)))
@@ -131,16 +133,16 @@ snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
 	data += sizeof(patch);
 
 	if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) {
-		snd_printk(KERN_ERR "The wrong kind of patch %x\n", patch.key);
+		dev_err(card->dev, "The wrong kind of patch %x\n", patch.key);
 		return -EINVAL;
 	}
 	if (count < patch.len) {
-		snd_printk(KERN_ERR "Patch too short %ld, need %d\n",
-			   count, patch.len);
+		dev_err(card->dev, "Patch too short %ld, need %d\n",
+			count, patch.len);
 		return -EINVAL;
 	}
 	if (patch.len < 0) {
-		snd_printk(KERN_ERR "poor length %d\n", patch.len);
+		dev_err(card->dev, "poor length %d\n", patch.len);
 		return -EINVAL;
 	}
 
@@ -164,7 +166,7 @@ snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
 	rc = -EINVAL;
 	switch (patch.type) {
 	case SNDRV_SFNT_LOAD_INFO:
-		rc = load_info(sflist, data, count);
+		rc = load_info(card, sflist, data, count);
 		break;
 	case SNDRV_SFNT_LOAD_DATA:
 		rc = load_data(sflist, data, count);
@@ -184,8 +186,8 @@ snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
 	case SNDRV_SFNT_REMOVE_INFO:
 		/* patch must be opened */
 		if (!sflist->currsf) {
-			snd_printk(KERN_ERR "soundfont: remove_info: "
-				   "patch not opened\n");
+			dev_err(card->dev,
+				"soundfont: remove_info: patch not opened\n");
 			rc = -EINVAL;
 		} else {
 			int bank, instr;
@@ -509,7 +511,8 @@ remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf,
  * open soundfont.
  */
 static int
-load_info(struct snd_sf_list *sflist, const void __user *data, long count)
+load_info(struct snd_card *card,
+	  struct snd_sf_list *sflist, const void __user *data, long count)
 {
 	struct snd_soundfont *sf;
 	struct snd_sf_zone *zone;
@@ -525,7 +528,7 @@ load_info(struct snd_sf_list *sflist, const void __user *data, long count)
 		return -EINVAL;
 
 	if (count < (long)sizeof(hdr)) {
-		printk(KERN_ERR "Soundfont error: invalid patch zone length\n");
+		dev_err(card->dev, "Soundfont error: invalid patch zone length\n");
 		return -EINVAL;
 	}
 	if (copy_from_user((char*)&hdr, data, sizeof(hdr)))
@@ -535,15 +538,15 @@ load_info(struct snd_sf_list *sflist, const void __user *data, long count)
 	count -= sizeof(hdr);
 
 	if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
-		printk(KERN_ERR "Soundfont error: Illegal voice number %d\n",
-		       hdr.nvoices);
+		dev_err(card->dev, "Soundfont error: Illegal voice number %d\n",
+			hdr.nvoices);
 		return -EINVAL;
 	}
 
 	if (count < (long)sizeof(struct soundfont_voice_info) * hdr.nvoices) {
-		printk(KERN_ERR "Soundfont Error: "
-		       "patch length(%ld) is smaller than nvoices(%d)\n",
-		       count, hdr.nvoices);
+		dev_err(card->dev,
+			"Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n",
+			count, hdr.nvoices);
 		return -EINVAL;
 	}
 
@@ -944,7 +947,8 @@ int snd_sf_vol_table[128] = {
 
 /* load GUS patch */
 static int
-load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count)
+load_guspatch(struct snd_card *card,
+	      struct snd_sf_list *sflist, const char __user *data, long count)
 {
 	struct patch_info patch;
 	struct snd_soundfont *sf;
@@ -954,7 +958,7 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count)
 	int rc;
 
 	if (count < (long)sizeof(patch)) {
-		snd_printk(KERN_ERR "patch record too small %ld\n", count);
+		dev_err(card->dev, "patch record too small %ld\n", count);
 		return -EINVAL;
 	}
 	if (copy_from_user(&patch, data, sizeof(patch)))
@@ -1041,10 +1045,10 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count)
 	/* panning position; -128 - 127 => 0-127 */
 	zone->v.pan = (patch.panning + 128) / 2;
 #if 0
-	snd_printk(KERN_DEBUG
-		   "gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n",
-		   (int)patch.base_freq, zone->v.rate_offset,
-		   zone->v.root, zone->v.tune, zone->v.low, zone->v.high);
+	pr_debug(
+		 "gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n",
+		 (int)patch.base_freq, zone->v.rate_offset,
+		 zone->v.root, zone->v.tune, zone->v.low, zone->v.high);
 #endif
 
 	/* detuning is ignored */
@@ -1076,12 +1080,12 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count)
 		zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release);
 		zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]);
 #if 0
-		snd_printk(KERN_DEBUG
-			   "gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n",
-			   zone->v.parm.volatkhld,
-			   zone->v.parm.voldcysus,
-			   zone->v.parm.volrelease,
-			   zone->v.attenuation);
+		dev_dbg(card->dev,
+			"gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n",
+			zone->v.parm.volatkhld,
+			zone->v.parm.voldcysus,
+			zone->v.parm.volrelease,
+			zone->v.attenuation);
 #endif
 	}
 
@@ -1125,12 +1129,13 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count)
 
 /* load GUS patch */
 int
-snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
+snd_soundfont_load_guspatch(struct snd_card *card,
+			    struct snd_sf_list *sflist, const char __user *data,
 			    long count)
 {
 	int rc;
 	lock_preset(sflist);
-	rc = load_guspatch(sflist, data, count);
+	rc = load_guspatch(card, sflist, data, count);
 	unlock_preset(sflist);
 	return rc;
 }
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 6.1.y 4/5] ALSA: synth: Use guard() for spin locks
  2026-11-10 23:19 ` [PATCH 6.1.y 1/5] ALSA: emux: prune unused parameter from snd_soundfont_load_guspatch() Sasha Levin
  2026-11-10 23:19   ` [PATCH 6.1.y 2/5] ALSA: emux: fix validation of snd_emux.num_ports Sasha Levin
  2026-11-10 23:19   ` [PATCH 6.1.y 3/5] ALSA: emux: Use standard print API Sasha Levin
@ 2026-11-10 23:19   ` Sasha Levin
  2026-11-10 23:19   ` [PATCH 6.1.y 5/5] ALSA: emux: Fix UAF of soundfont zones at note-on Sasha Levin
  3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-10 23:19 UTC (permalink / raw)
  To: stable; +Cc: Takashi Iwai, Sasha Levin

From: Takashi Iwai <tiwai@suse.de>

[ Upstream commit 59ede7178d402b3bfa0266cbd9ad348d5612c295 ]

Clean up the code using guard() for spin locks.

Merely code refactoring, and no behavior change.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20250829151335.7342-5-tiwai@suse.de
Stable-dep-of: 34e3e04b8a17 ("ALSA: emux: Fix UAF of soundfont zones at note-on")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 sound/synth/emux/emux_effect.c | 29 ++++++++--------
 sound/synth/emux/emux_synth.c  | 54 +++++++----------------------
 sound/synth/emux/soundfont.c   | 63 ++++++++++++----------------------
 3 files changed, 49 insertions(+), 97 deletions(-)

diff --git a/sound/synth/emux/emux_effect.c b/sound/synth/emux/emux_effect.c
index 3c7314f5fb19c..bfe383fa90ba1 100644
--- a/sound/synth/emux/emux_effect.c
+++ b/sound/synth/emux/emux_effect.c
@@ -168,7 +168,6 @@ snd_emux_send_effect(struct snd_emux_port *port, struct snd_midi_channel *chan,
 	unsigned char *srcp, *origp;
 	struct snd_emux *emu;
 	struct snd_emux_effect_table *fx;
-	unsigned long flags;
 
 	emu = port->emu;
 	fx = chan->private;
@@ -195,22 +194,22 @@ snd_emux_send_effect(struct snd_emux_port *port, struct snd_midi_channel *chan,
 		offset++;
 #endif
 	/* modify the register values */
-	spin_lock_irqsave(&emu->voice_lock, flags);
-	for (i = 0; i < emu->max_voices; i++) {
-		struct snd_emux_voice *vp = &emu->voices[i];
-		if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan)
-			continue;
-		srcp = (unsigned char*)&vp->reg.parm + offset;
-		origp = (unsigned char*)&vp->zone->v.parm + offset;
-		if (parm_defs[i].type & PARM_IS_BYTE) {
-			*srcp = *origp;
-			effect_set_byte(srcp, chan, type);
-		} else {
-			*(unsigned short*)srcp = *(unsigned short*)origp;
-			effect_set_word((unsigned short*)srcp, chan, type);
+	scoped_guard(spinlock_irqsave, &emu->voice_lock) {
+		for (i = 0; i < emu->max_voices; i++) {
+			struct snd_emux_voice *vp = &emu->voices[i];
+			if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan)
+				continue;
+			srcp = (unsigned char *)&vp->reg.parm + offset;
+			origp = (unsigned char *)&vp->zone->v.parm + offset;
+			if (parm_defs[i].type & PARM_IS_BYTE) {
+				*srcp = *origp;
+				effect_set_byte(srcp, chan, type);
+			} else {
+				*(unsigned short *)srcp = *(unsigned short *)origp;
+				effect_set_word((unsigned short *)srcp, chan, type);
+			}
 		}
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 
 	/* activate them */
 	snd_emux_update_channel(port, chan, parm_defs[type].update);
diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
index ebf672cdbc31b..a541be1c41890 100644
--- a/sound/synth/emux/emux_synth.c
+++ b/sound/synth/emux/emux_synth.c
@@ -49,7 +49,6 @@ snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
 	int i, key, nvoices;
 	struct snd_emux_voice *vp;
 	struct snd_sf_zone *table[SNDRV_EMUX_MAX_MULTI_VOICES];
-	unsigned long flags;
 	struct snd_emux_port *port;
 
 	port = p;
@@ -77,7 +76,7 @@ snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
 	terminate_note1(emu, key, chan, 0);
 #endif
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (i = 0; i < nvoices; i++) {
 
 		/* set up each voice parameter */
@@ -124,7 +123,6 @@ snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
 			vp->ontime = jiffies; /* remember the trigger timing */
 		}
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 
 #ifdef SNDRV_EMUX_USE_RAW_EFFECT
 	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
@@ -147,7 +145,6 @@ snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
 	int ch;
 	struct snd_emux *emu;
 	struct snd_emux_voice *vp;
-	unsigned long flags;
 	struct snd_emux_port *port;
 
 	port = p;
@@ -158,7 +155,7 @@ snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
 	if (snd_BUG_ON(!emu || !emu->ops.release))
 		return;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (ch = 0; ch < emu->max_voices; ch++) {
 		vp = &emu->voices[ch];
 		if (STATE_IS_PLAYING(vp->state) &&
@@ -180,7 +177,6 @@ snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
 				emu->ops.release(vp);
 		}
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 /*
@@ -192,10 +188,9 @@ void snd_emux_timer_callback(struct timer_list *t)
 {
 	struct snd_emux *emu = from_timer(emu, t, tlist);
 	struct snd_emux_voice *vp;
-	unsigned long flags;
 	int ch, do_again = 0;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (ch = 0; ch < emu->max_voices; ch++) {
 		vp = &emu->voices[ch];
 		if (vp->state == SNDRV_EMUX_ST_PENDING) {
@@ -212,7 +207,6 @@ void snd_emux_timer_callback(struct timer_list *t)
 		emu->timer_active = 1;
 	} else
 		emu->timer_active = 0;
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 /*
@@ -224,7 +218,6 @@ snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
 	int ch;
 	struct snd_emux *emu;
 	struct snd_emux_voice *vp;
-	unsigned long flags;
 	struct snd_emux_port *port;
 
 	port = p;
@@ -235,7 +228,7 @@ snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
 	if (snd_BUG_ON(!emu || !emu->ops.update))
 		return;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (ch = 0; ch < emu->max_voices; ch++) {
 		vp = &emu->voices[ch];
 		if (vp->state == SNDRV_EMUX_ST_ON &&
@@ -244,7 +237,6 @@ snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
 			update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME);
 		}
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 
@@ -257,7 +249,6 @@ snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *cha
 	struct snd_emux *emu;
 	struct snd_emux_voice *vp;
 	int i;
-	unsigned long flags;
 
 	if (! update)
 		return;
@@ -266,13 +257,12 @@ snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *cha
 	if (snd_BUG_ON(!emu || !emu->ops.update))
 		return;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (i = 0; i < emu->max_voices; i++) {
 		vp = &emu->voices[i];
 		if (vp->chan == chan)
 			update_voice(emu, vp, update);
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 /*
@@ -284,7 +274,6 @@ snd_emux_update_port(struct snd_emux_port *port, int update)
 	struct snd_emux *emu; 
 	struct snd_emux_voice *vp;
 	int i;
-	unsigned long flags;
 
 	if (! update)
 		return;
@@ -293,13 +282,12 @@ snd_emux_update_port(struct snd_emux_port *port, int update)
 	if (snd_BUG_ON(!emu || !emu->ops.update))
 		return;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (i = 0; i < emu->max_voices; i++) {
 		vp = &emu->voices[i];
 		if (vp->port == port)
 			update_voice(emu, vp, update);
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 
@@ -365,16 +353,14 @@ terminate_note1(struct snd_emux *emu, int note, struct snd_midi_channel *chan, i
 {
 	int  i;
 	struct snd_emux_voice *vp;
-	unsigned long flags;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (i = 0; i < emu->max_voices; i++) {
 		vp = &emu->voices[i];
 		if (STATE_IS_PLAYING(vp->state) && vp->chan == chan &&
 		    vp->key == note)
 			terminate_voice(emu, vp, free);
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 
@@ -407,9 +393,8 @@ snd_emux_terminate_all(struct snd_emux *emu)
 {
 	int i;
 	struct snd_emux_voice *vp;
-	unsigned long flags;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (i = 0; i < emu->max_voices; i++) {
 		vp = &emu->voices[i];
 		if (STATE_IS_PLAYING(vp->state))
@@ -424,7 +409,6 @@ snd_emux_terminate_all(struct snd_emux *emu)
 	}
 	/* initialize allocation time */
 	emu->use_time = 0;
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 EXPORT_SYMBOL(snd_emux_terminate_all);
@@ -438,7 +422,6 @@ snd_emux_sounds_off_all(struct snd_emux_port *port)
 	int i;
 	struct snd_emux *emu;
 	struct snd_emux_voice *vp;
-	unsigned long flags;
 
 	if (snd_BUG_ON(!port))
 		return;
@@ -446,7 +429,7 @@ snd_emux_sounds_off_all(struct snd_emux_port *port)
 	if (snd_BUG_ON(!emu || !emu->ops.terminate))
 		return;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (i = 0; i < emu->max_voices; i++) {
 		vp = &emu->voices[i];
 		if (STATE_IS_PLAYING(vp->state) &&
@@ -459,7 +442,6 @@ snd_emux_sounds_off_all(struct snd_emux_port *port)
 				emu->ops.reset(emu, i);
 		}
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 
@@ -472,9 +454,8 @@ exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port, int exclass
 {
 	struct snd_emux_voice *vp;
 	int  i;
-	unsigned long flags;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (i = 0; i < emu->max_voices; i++) {
 		vp = &emu->voices[i];
 		if (STATE_IS_PLAYING(vp->state) && vp->port == port &&
@@ -482,7 +463,6 @@ exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port, int exclass
 			terminate_voice(emu, vp, 0);
 		}
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 /*
@@ -915,9 +895,8 @@ snd_emux_init_voices(struct snd_emux *emu)
 {
 	struct snd_emux_voice *vp;
 	int i;
-	unsigned long flags;
 
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	for (i = 0; i < emu->max_voices; i++) {
 		vp = &emu->voices[i];
 		vp->ch = -1; /* not used */
@@ -928,23 +907,19 @@ snd_emux_init_voices(struct snd_emux *emu)
 		vp->emu = emu;
 		vp->hw = emu->hw;
 	}
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 /*
  */
 void snd_emux_lock_voice(struct snd_emux *emu, int voice)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF)
 		emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED;
 	else
 		dev_warn(emu->card->dev,
 			 "invalid voice for lock %d (state = %x)\n",
 			 voice, emu->voices[voice].state);
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 EXPORT_SYMBOL(snd_emux_lock_voice);
@@ -953,16 +928,13 @@ EXPORT_SYMBOL(snd_emux_lock_voice);
  */
 void snd_emux_unlock_voice(struct snd_emux *emu, int voice)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&emu->voice_lock, flags);
+	guard(spinlock_irqsave)(&emu->voice_lock);
 	if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED)
 		emu->voices[voice].state = SNDRV_EMUX_ST_OFF;
 	else
 		dev_warn(emu->card->dev,
 			 "invalid voice for unlock %d (state = %x)\n",
 			 voice, emu->voices[voice].state);
-	spin_unlock_irqrestore(&emu->voice_lock, flags);
 }
 
 EXPORT_SYMBOL(snd_emux_unlock_voice);
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 03f70cceb4a86..c124d6c27a945 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -66,11 +66,9 @@ static void snd_sf_clear(struct snd_sf_list *sflist);
 static void
 lock_preset(struct snd_sf_list *sflist)
 {
-	unsigned long flags;
 	mutex_lock(&sflist->presets_mutex);
-	spin_lock_irqsave(&sflist->lock, flags);
+	guard(spinlock_irqsave)(&sflist->lock);
 	sflist->presets_locked = 1;
-	spin_unlock_irqrestore(&sflist->lock, flags);
 }
 
 
@@ -80,10 +78,8 @@ lock_preset(struct snd_sf_list *sflist)
 static void
 unlock_preset(struct snd_sf_list *sflist)
 {
-	unsigned long flags;
-	spin_lock_irqsave(&sflist->lock, flags);
+	guard(spinlock_irqsave)(&sflist->lock);
 	sflist->presets_locked = 0;
-	spin_unlock_irqrestore(&sflist->lock, flags);
 	mutex_unlock(&sflist->presets_mutex);
 }
 
@@ -94,14 +90,11 @@ unlock_preset(struct snd_sf_list *sflist)
 int
 snd_soundfont_close_check(struct snd_sf_list *sflist, int client)
 {
-	unsigned long flags;
-	spin_lock_irqsave(&sflist->lock, flags);
-	if (sflist->open_client == client)  {
-		spin_unlock_irqrestore(&sflist->lock, flags);
-		return close_patch(sflist);
+	scoped_guard(spinlock_irqsave, &sflist->lock) {
+		if (sflist->open_client != client)
+			return 0;
 	}
-	spin_unlock_irqrestore(&sflist->lock, flags);
-	return 0;
+	return close_patch(sflist);
 }
 
 
@@ -119,7 +112,6 @@ snd_soundfont_load(struct snd_card *card,
 		   long count, int client)
 {
 	struct soundfont_patch_info patch;
-	unsigned long flags;
 	int  rc;
 
 	if (count < (long)sizeof(patch)) {
@@ -155,12 +147,10 @@ snd_soundfont_load(struct snd_card *card,
 	}
 
 	/* check if other client already opened patch */
-	spin_lock_irqsave(&sflist->lock, flags);
-	if (sflist->open_client != client) {
-		spin_unlock_irqrestore(&sflist->lock, flags);
-		return -EBUSY;
+	scoped_guard(spinlock_irqsave, &sflist->lock) {
+		if (sflist->open_client != client)
+			return -EBUSY;
 	}
-	spin_unlock_irqrestore(&sflist->lock, flags);
 
 	lock_preset(sflist);
 	rc = -EINVAL;
@@ -223,14 +213,11 @@ open_patch(struct snd_sf_list *sflist, const char __user *data,
 {
 	struct soundfont_open_parm parm;
 	struct snd_soundfont *sf;
-	unsigned long flags;
 
-	spin_lock_irqsave(&sflist->lock, flags);
-	if (sflist->open_client >= 0 || sflist->currsf) {
-		spin_unlock_irqrestore(&sflist->lock, flags);
-		return -EBUSY;
+	scoped_guard(spinlock_irqsave, &sflist->lock) {
+		if (sflist->open_client >= 0 || sflist->currsf)
+			return -EBUSY;
 	}
-	spin_unlock_irqrestore(&sflist->lock, flags);
 
 	if (copy_from_user(&parm, data, sizeof(parm)))
 		return -EFAULT;
@@ -244,10 +231,10 @@ open_patch(struct snd_sf_list *sflist, const char __user *data,
 		return -ENOMEM;
 	}
 
-	spin_lock_irqsave(&sflist->lock, flags);
-	sflist->open_client = client;
-	sflist->currsf = sf;
-	spin_unlock_irqrestore(&sflist->lock, flags);
+	scoped_guard(spinlock_irqsave, &sflist->lock) {
+		sflist->open_client = client;
+		sflist->currsf = sf;
+	}
 
 	return 0;
 }
@@ -305,12 +292,10 @@ is_identical_font(struct snd_soundfont *sf, int type, unsigned char *name)
 static int
 close_patch(struct snd_sf_list *sflist)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&sflist->lock, flags);
-	sflist->currsf = NULL;
-	sflist->open_client = -1;
-	spin_unlock_irqrestore(&sflist->lock, flags);
+	scoped_guard(spinlock_irqsave, &sflist->lock) {
+		sflist->currsf = NULL;
+		sflist->open_client = -1;
+	}
 
 	rebuild_presets(sflist);
 
@@ -1243,17 +1228,14 @@ snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
 			  struct snd_sf_zone **table, int max_layers)
 {
 	int nvoices;
-	unsigned long flags;
 
 	/* this function is supposed to be called atomically,
 	 * so we check the lock.  if it's busy, just returns 0 to
 	 * tell the caller the busy state
 	 */
-	spin_lock_irqsave(&sflist->lock, flags);
-	if (sflist->presets_locked) {
-		spin_unlock_irqrestore(&sflist->lock, flags);
+	guard(spinlock_irqsave)(&sflist->lock);
+	if (sflist->presets_locked)
 		return 0;
-	}
 	nvoices = search_zones(sflist, notep, vel, preset, bank,
 			       table, max_layers, 0);
 	if (! nvoices) {
@@ -1262,7 +1244,6 @@ snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
 					       def_preset, def_bank,
 					       table, max_layers, 0);
 	}
-	spin_unlock_irqrestore(&sflist->lock, flags);
 	return nvoices;
 }
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 6.1.y 5/5] ALSA: emux: Fix UAF of soundfont zones at note-on
  2026-11-10 23:19 ` [PATCH 6.1.y 1/5] ALSA: emux: prune unused parameter from snd_soundfont_load_guspatch() Sasha Levin
                     ` (2 preceding siblings ...)
  2026-11-10 23:19   ` [PATCH 6.1.y 4/5] ALSA: synth: Use guard() for spin locks Sasha Levin
@ 2026-11-10 23:19   ` Sasha Levin
  3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-10 23:19 UTC (permalink / raw)
  To: stable; +Cc: Takashi Iwai, Sashiko, Sasha Levin

From: Takashi Iwai <tiwai@suse.de>

[ Upstream commit 34e3e04b8a1731a773797542f0781e99ce7cd755 ]

snd_emux_note_on() looks up the matching zones via
snd_soundfont_search_zone(), which takes sflist->lock only during the
lookup and returns the raw zone pointers after releasing it.  The
caller then stores them in vp->zone without any protection.
Meanwhile, a concurrent SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES ioctl may
unlink and free those zones in snd_soundfont_remove_unlocked(); this
leads to a use-after-free in snd_emux_note_on(), and later at
accessing the cached vp->zone, e.g. in snd_emux_send_effect().

Fix it by holding sflist->lock in snd_emux_note_on() over the whole
zone lookup and voice setup, and let snd_soundfont_search_zone()
require the caller to hold the lock, instead.
snd_soundfont_lock_preset() takes sflist->lock for setting
presets_locked, so the removal path now syncs with the note-on finish.

Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/bug/linux-697212bb-a434-4ff5-8778-25a84f5dad89
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261020101624.205638-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 sound/synth/emux/emux_synth.c | 3 +++
 sound/synth/emux/soundfont.c  | 9 ++++++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
index a541be1c41890..2ce6dc4000fc2 100644
--- a/sound/synth/emux/emux_synth.c
+++ b/sound/synth/emux/emux_synth.c
@@ -60,6 +60,9 @@ snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
 		return;
 
 	key = note; /* remember the original note */
+
+	/* hold sflist->lock for protecting against concurrent free */
+	guard(spinlock_irqsave)(&emu->sflist->lock);
 	nvoices = get_zone(emu, port, &note, vel, chan, table);
 	if (! nvoices)
 		return;
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index c124d6c27a945..2e37d91153a05 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -1220,6 +1220,9 @@ delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp)
  * The found zones are stored on 'table' array.  max_layers defines
  * the maximum number of elements in this array.
  * This function returns the number of found zones.  0 if not found.
+ *
+ * The caller must hold sflist->lock, and keep holding it as long as
+ * the returned zones are accessed.
  */
 int
 snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
@@ -1229,11 +1232,11 @@ snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
 {
 	int nvoices;
 
-	/* this function is supposed to be called atomically,
-	 * so we check the lock.  if it's busy, just returns 0 to
+	lockdep_assert_held(&sflist->lock);
+
+	/* if the presets are being modified, just returns 0 to
 	 * tell the caller the busy state
 	 */
-	guard(spinlock_irqsave)(&sflist->lock);
 	if (sflist->presets_locked)
 		return 0;
 	nvoices = search_zones(sflist, notep, vel, preset, bank,
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-10 23:19 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-07 10:52 FAILED: patch "[PATCH] ALSA: emux: Fix UAF of soundfont zones at note-on" failed to apply to 6.1-stable tree Sasha Levin
2026-11-10 23:19 ` [PATCH 6.1.y 1/5] ALSA: emux: prune unused parameter from snd_soundfont_load_guspatch() Sasha Levin
2026-11-10 23:19   ` [PATCH 6.1.y 2/5] ALSA: emux: fix validation of snd_emux.num_ports Sasha Levin
2026-11-10 23:19   ` [PATCH 6.1.y 3/5] ALSA: emux: Use standard print API Sasha Levin
2026-11-10 23:19   ` [PATCH 6.1.y 4/5] ALSA: synth: Use guard() for spin locks Sasha Levin
2026-11-10 23:19   ` [PATCH 6.1.y 5/5] ALSA: emux: Fix UAF of soundfont zones at note-on Sasha Levin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox