ALSA: usb-audio: Protect Roland control activation

The USB MIDI driver changes the Roland MIDI Input Mode control's access
flags directly from the rawmidi open and close paths.  These changes are
not protected by the ALSA control core and the corresponding notifications
can race with control access.

Use snd_ctl_activate_id() so that the control core updates the access flags
and sends the notification under its lock.  Release the USB MIDI mutex
before calling it because the control write path holds controls_rwsem while
roland_load_put() takes the USB MIDI mutex.

Keep the state transition and alternate-setting change under the USB MIDI
mutex; rawmidi's open mutex serializes the enclosing open and close paths.

Fixes: 96f61d9ade ("sound: usb-audio: allow switching altsetting on Roland USB MIDI devices")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
Link: https://patch.msgid.link/20260929141726.2166899-1-runyu.xiao@seu.edu.cn
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Runyu Xiao 2026-09-29 22:17:26 +08:00 committed by Takashi Iwai
parent 025877a383
commit 19b42c1fff

View File

@ -1150,41 +1150,42 @@ static int substream_open(struct snd_rawmidi_substream *substream, int dir,
int open)
{
struct snd_usb_midi *umidi = substream->rmidi->private_data;
struct snd_kcontrol *ctl;
struct snd_ctl_elem_id ctl_id;
bool activate_ctl = false;
bool active;
guard(rwsem_read)(&umidi->disc_rwsem);
if (umidi->disconnected)
return open ? -ENODEV : 0;
guard(mutex)(&umidi->mutex);
if (open) {
if (!umidi->opened[0] && !umidi->opened[1]) {
if (umidi->roland_load_ctl) {
ctl = umidi->roland_load_ctl;
ctl->vd[0].access |=
SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(umidi->card,
SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
update_roland_altsetting(umidi);
scoped_guard(mutex, &umidi->mutex) {
if (open) {
if (!umidi->opened[0] && !umidi->opened[1]) {
if (umidi->roland_load_ctl) {
ctl_id = umidi->roland_load_ctl->id;
activate_ctl = true;
active = false;
update_roland_altsetting(umidi);
}
}
}
umidi->opened[dir]++;
if (umidi->opened[1])
snd_usbmidi_input_start(&umidi->list);
} else {
umidi->opened[dir]--;
if (!umidi->opened[1] && !umidi->keep_input_running)
snd_usbmidi_input_stop(&umidi->list);
if (!umidi->opened[0] && !umidi->opened[1]) {
if (umidi->roland_load_ctl) {
ctl = umidi->roland_load_ctl;
ctl->vd[0].access &=
~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(umidi->card,
SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
umidi->opened[dir]++;
if (umidi->opened[1])
snd_usbmidi_input_start(&umidi->list);
} else {
umidi->opened[dir]--;
if (!umidi->opened[1] && !umidi->keep_input_running)
snd_usbmidi_input_stop(&umidi->list);
if (!umidi->opened[0] && !umidi->opened[1]) {
if (umidi->roland_load_ctl) {
ctl_id = umidi->roland_load_ctl->id;
activate_ctl = true;
active = true;
}
}
}
}
if (activate_ctl)
snd_ctl_activate_id(umidi->card, &ctl_id, active);
return 0;
}