ALSA: emux: Stop voices before freeing zones in all cases

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>
This commit is contained in:
Takashi Iwai 2026-10-20 12:16:21 +02:00
parent 34e3e04b8a
commit 8a5d87c2ba
2 changed files with 16 additions and 3 deletions

View File

@ -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)
@ -95,8 +100,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;

View File

@ -443,6 +443,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;