* [PATCH 5.10.y 2/4] ALSA: emux: centralize & improve patch info validation
2026-11-11 2:07 ` [PATCH 5.10.y 1/4] ALSA: emu10k1: prune vestiges of SNDRV_SFNT_SAMPLE_{BIDIR,REVERSE}_LOOP support Sasha Levin
@ 2026-11-11 2:07 ` Sasha Levin
2026-11-11 2:07 ` [PATCH 5.10.y 3/4] ALSA: emux: simplify snd_sf_list.callback handling Sasha Levin
2026-11-11 2:07 ` [PATCH 5.10.y 4/4] ALSA: emux: Stop voices before freeing zones in all cases Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-11-11 2:07 UTC (permalink / raw)
To: stable; +Cc: Oswald Buddenhagen, Takashi Iwai, Sasha Levin
From: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
[ Upstream commit de67aab120d4d5ba7d9e94ee5b25464ae0d1bd0e ]
This does several closely related things:
- Move the code from the drivers into the SoundFont loader, which
de-duplicates it.
- Sort of explain the weird "recalculate address offset" feature. Note
that I don't think it actually makes any sense - the calling user
space code should do that. The background is certainly that the source
data (the SoundFont format) uses pointers into a single wave block
(and the API allows doing the same for on-board ROM), but the API
expects the wave data from user space to be pre-chopped into
individual patches anyway.
- Make sure that the specified offsets actually lie within the supplied
wave data. Note that we don't validate ROM offsets, so one can play
back anything within the sound card's address space.
- In load_guspatch(), don't call the sample_new callback anymore when
the patch size is zero, as was already the case in load_data(). The
callbacks would instantly return in that case anyway; these checks are
now removed.
Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Message-ID: <20240406064830.1029573-7-oswald.buddenhagen@gmx.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Stable-dep-of: 8a5d87c2ba30 ("ALSA: emux: Stop voices before freeing zones in all cases")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
sound/isa/sb/emu8000_patch.c | 13 -----------
sound/pci/emu10k1/emu10k1_patch.c | 16 -------------
sound/synth/emux/soundfont.c | 37 ++++++++++++++++++++++++++++++-
3 files changed, 36 insertions(+), 30 deletions(-)
diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
index 0cb94cafb4c9b..b8b9edd98ac6a 100644
--- a/sound/isa/sb/emu8000_patch.c
+++ b/sound/isa/sb/emu8000_patch.c
@@ -148,13 +148,6 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
if (snd_BUG_ON(!sp))
return -EINVAL;
- if (sp->v.size == 0)
- return 0;
-
- /* be sure loop points start < end */
- if (sp->v.loopstart > sp->v.loopend)
- swap(sp->v.loopstart, sp->v.loopend);
-
/* compute true data size to be loaded */
truesize = sp->v.size;
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
@@ -177,12 +170,6 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
return -EFAULT;
}
- /* recalculate address offset */
- sp->v.end -= sp->v.start;
- sp->v.loopstart -= sp->v.start;
- sp->v.loopend -= sp->v.start;
- sp->v.start = 0;
-
/* dram position (in word) -- mem_offset is byte */
dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
dram_start = dram_offset;
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
index 49214c2268084..47d69a0e44bc6 100644
--- a/sound/pci/emu10k1/emu10k1_patch.c
+++ b/sound/pci/emu10k1/emu10k1_patch.c
@@ -35,28 +35,12 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
if (snd_BUG_ON(!sp || !hdr))
return -EINVAL;
- if (sp->v.size == 0) {
- dev_dbg(emu->card->dev,
- "emu: rom font for sample %d\n", sp->v.sample);
- return 0;
- }
-
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
/* should instead return -ENOTSUPP; but compatibility */
printk(KERN_WARNING "Emu10k1 wavetable patch %d with unsupported loop feature\n",
sp->v.sample);
}
- /* recalculate address offset */
- sp->v.end -= sp->v.start;
- sp->v.loopstart -= sp->v.start;
- sp->v.loopend -= sp->v.start;
- sp->v.start = 0;
-
- /* be sure loop points start < end */
- if (sp->v.loopstart >= sp->v.loopend)
- swap(sp->v.loopstart, sp->v.loopend);
-
/* compute true data size to be loaded */
truesize = sp->v.size + BLANK_HEAD_SIZE;
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 18b2d84926563..469aa974f8fe0 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -685,6 +685,21 @@ find_sample(struct snd_soundfont *sf, int sample_id)
}
+static int
+validate_sample_info(struct soundfont_sample_info *si)
+{
+ if (si->end < 0 || si->end > si->size)
+ return -EINVAL;
+ if (si->loopstart < 0 || si->loopstart > si->end)
+ return -EINVAL;
+ if (si->loopend < 0 || si->loopend > si->end)
+ return -EINVAL;
+ /* be sure loop points start < end */
+ if (si->loopstart > si->loopend)
+ swap(si->loopstart, si->loopend);
+ return 0;
+}
+
/*
* Load sample information, this can include data to be loaded onto
* the soundcard. It can also just be a pointer into soundcard ROM.
@@ -725,6 +740,21 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count)
return -EINVAL;
}
+ if (sample_info.size > 0) {
+ if (sample_info.start < 0)
+ return -EINVAL;
+
+ // Here we "rebase out" the start address, because the
+ // real start is the start of the provided sample data.
+ sample_info.end -= sample_info.start;
+ sample_info.loopstart -= sample_info.start;
+ sample_info.loopend -= sample_info.start;
+ sample_info.start = 0;
+
+ if (validate_sample_info(&sample_info) < 0)
+ return -EINVAL;
+ }
+
/* Allocate a new sample structure */
if ((sp = sf_sample_new(sflist, sf)) == NULL)
return -ENOMEM;
@@ -973,6 +1003,11 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
smp->v.loopend = patch.loop_end;
smp->v.size = patch.len;
+ if (validate_sample_info(&smp->v) < 0) {
+ sf_sample_delete(sflist, sf, smp);
+ return -EINVAL;
+ }
+
/* set up mode flags */
smp->v.mode_flags = 0;
if (!(patch.mode & WAVE_16_BITS))
@@ -1009,7 +1044,7 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
/*
* load wave data
*/
- if (sflist->callback.sample_new) {
+ if (smp->v.size > 0 && sflist->callback.sample_new) {
rc = sflist->callback.sample_new
(sflist->callback.private_data, smp, sflist->memhdr,
data, count);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 5.10.y 3/4] ALSA: emux: simplify snd_sf_list.callback handling
2026-11-11 2:07 ` [PATCH 5.10.y 1/4] ALSA: emu10k1: prune vestiges of SNDRV_SFNT_SAMPLE_{BIDIR,REVERSE}_LOOP support Sasha Levin
2026-11-11 2:07 ` [PATCH 5.10.y 2/4] ALSA: emux: centralize & improve patch info validation Sasha Levin
@ 2026-11-11 2:07 ` Sasha Levin
2026-11-11 2:07 ` [PATCH 5.10.y 4/4] ALSA: emux: Stop voices before freeing zones in all cases Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-11-11 2:07 UTC (permalink / raw)
To: stable; +Cc: Oswald Buddenhagen, Takashi Iwai, Sasha Levin
From: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
[ Upstream commit 4c4cbe66828f185903d2127aed6ac9b7302e9d3a ]
Both drivers provide both sample_new and sample_free, and it makes no
sense to pretend that they could not. In fact, load_data() would already
crash if sample_new was null. So remove the remaining null checks.
Contrary to that, the emu10k1 driver actually has a null sample_reset,
though I'm not convinced that this inconsistency is justified.
Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Message-ID: <20240406064830.1029573-18-oswald.buddenhagen@gmx.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Stable-dep-of: 8a5d87c2ba30 ("ALSA: emux: Stop voices before freeing zones in all cases")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
sound/synth/emux/emux.c | 6 ++----
sound/synth/emux/soundfont.c | 12 +++++-------
2 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index c60ff81390a44..8e080a4bcd16f 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -94,10 +94,8 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch
/* create soundfont list */
memset(&sf_cb, 0, sizeof(sf_cb));
sf_cb.private_data = emu;
- if (emu->ops.sample_new)
- sf_cb.sample_new = sf_sample_new;
- if (emu->ops.sample_free)
- sf_cb.sample_free = sf_sample_free;
+ sf_cb.sample_new = sf_sample_new;
+ sf_cb.sample_free = sf_sample_free;
if (emu->ops.sample_reset)
sf_cb.sample_reset = sf_sample_reset;
emu->sflist = snd_sf_new(&sf_cb, emu->memhdr);
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 469aa974f8fe0..0a981b636537b 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -1044,7 +1044,7 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
/*
* load wave data
*/
- if (smp->v.size > 0 && sflist->callback.sample_new) {
+ if (smp->v.size > 0) {
rc = sflist->callback.sample_new
(sflist->callback.private_data, smp, sflist->memhdr,
data, count);
@@ -1406,9 +1406,8 @@ snd_sf_clear(struct snd_sf_list *sflist)
}
for (sp = sf->samples; sp; sp = nextsp) {
nextsp = sp->next;
- if (sflist->callback.sample_free)
- sflist->callback.sample_free(sflist->callback.private_data,
- sp, sflist->memhdr);
+ sflist->callback.sample_free(sflist->callback.private_data,
+ sp, sflist->memhdr);
kfree(sp);
}
kfree(sf);
@@ -1509,9 +1508,8 @@ snd_soundfont_remove_unlocked(struct snd_sf_list *sflist)
nextsp = sp->next;
sf->samples = nextsp;
sflist->mem_used -= sp->v.truesize;
- if (sflist->callback.sample_free)
- sflist->callback.sample_free(sflist->callback.private_data,
- sp, sflist->memhdr);
+ sflist->callback.sample_free(sflist->callback.private_data,
+ sp, sflist->memhdr);
kfree(sp);
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 5.10.y 4/4] ALSA: emux: Stop voices before freeing zones in all cases
2026-11-11 2:07 ` [PATCH 5.10.y 1/4] ALSA: emu10k1: prune vestiges of SNDRV_SFNT_SAMPLE_{BIDIR,REVERSE}_LOOP support Sasha Levin
2026-11-11 2:07 ` [PATCH 5.10.y 2/4] ALSA: emux: centralize & improve patch info validation Sasha Levin
2026-11-11 2:07 ` [PATCH 5.10.y 3/4] ALSA: emux: simplify snd_sf_list.callback handling Sasha Levin
@ 2026-11-11 2:07 ` Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-11-11 2:07 UTC (permalink / raw)
To: stable; +Cc: Takashi Iwai, Sasha Levin
From: Takashi Iwai <tiwai@suse.de>
[ Upstream commit 8a5d87c2ba30e8de31cfbe888fd4d95f43c63db9 ]
The emux voice caches the soundfont zone pointer in vp->zone, which
is referred to later, e.g. in snd_emux_send_effect(). For avoiding
dangling pointers, the soundfont code calls the sample_reset callback
to stop all voices before freeing zones. However, in a few cases
(e.g. emu10k1's missing sample_reset or the call path via
remove_info()), the sample reset isn't called properly, leaving the
playing voices.
Fix it by always registering the sample_reset callback, falling back
to snd_emux_terminate_all() when the driver has no own ops, and by
calling sample_reset in remove_info() before freeing the zones.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261020101624.205638-2-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
sound/synth/emux/emux.c | 10 +++++++---
sound/synth/emux/soundfont.c | 9 +++++++++
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index 8e080a4bcd16f..cfd472effe88d 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -71,7 +71,12 @@ static int sf_sample_free(void *private_data, struct snd_sf_sample *sp,
static void sf_sample_reset(void *private_data)
{
struct snd_emux *emu = private_data;
- emu->ops.sample_reset(emu);
+
+ /* stop all voices that might refer to zones and samples to be freed */
+ if (emu->ops.sample_reset)
+ emu->ops.sample_reset(emu);
+ else
+ snd_emux_terminate_all(emu);
}
int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, char *name)
@@ -96,8 +101,7 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch
sf_cb.private_data = emu;
sf_cb.sample_new = sf_sample_new;
sf_cb.sample_free = sf_sample_free;
- if (emu->ops.sample_reset)
- sf_cb.sample_reset = sf_sample_reset;
+ sf_cb.sample_reset = sf_sample_reset;
emu->sflist = snd_sf_new(&sf_cb, emu->memhdr);
if (emu->sflist == NULL)
return -ENOMEM;
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 0a981b636537b..163008b2ee14a 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -480,6 +480,15 @@ remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf,
struct snd_sf_zone *prev, *next, *p;
int removed = 0;
+ /* stop voices before freeing the zones they might refer to */
+ for (p = sf->zones; p; p = p->next) {
+ if (!p->mapped && p->bank == bank && p->instr == instr) {
+ if (sflist->callback.sample_reset)
+ sflist->callback.sample_reset(sflist->callback.private_data);
+ break;
+ }
+ }
+
prev = NULL;
for (p = sf->zones; p; p = next) {
next = p->next;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread