From a718fd03e1e00211cf4662f5ab82d33f4c18e2a7 Mon Sep 17 00:00:00 2001 From: Niko Huuskonen Date: Sun, 27 Sep 2026 03:35:29 +0300 Subject: [PATCH] ALSA: caiaq: Serialize access to the EP1 command buffer snd_usb_caiaq_send_command() and snd_usb_caiaq_send_command_bank() copy the command into cdev->ep1_out_buf and send it with a synchronous bulk transfer. Nothing serializes their callers. An ALSA control write, which sets the LEDs on the Kore controllers and several other devices, can run at the same time as a PCM prepare, which sends the audio parameters through the same buffer. One caller can then overwrite the buffer while the transfer of the other is still in flight, and the device receives a mix of both commands. Protect the buffer with a mutex. All callers run in process context and already sleep in usb_bulk_msg(). The problem was found by code review while adding another user of the buffer, the Kore LCD support later in this series. It has not been observed or reproduced. Fixes: 8e3cd08ed8e5 ("[ALSA] caiaq - add control API and more input features") Assisted-by: LLM Signed-off-by: Niko Huuskonen Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260927003532.289468-2-niko.huuskonen.00@gmail.com --- sound/usb/caiaq/device.c | 5 +++++ sound/usb/caiaq/device.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index f604846ec6e6..e8619bb74358 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -212,6 +212,8 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev, if (len > EP1_BUFSIZE - 1) len = EP1_BUFSIZE - 1; + guard(mutex)(&cdev->ep1_out_mutex); + if (buffer && len > 0) memcpy(cdev->ep1_out_buf+1, buffer, len); @@ -235,6 +237,8 @@ int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev, if (len > EP1_BUFSIZE - 2) len = EP1_BUFSIZE - 2; + guard(mutex)(&cdev->ep1_out_mutex); + if (buffer && len > 0) memcpy(cdev->ep1_out_buf+2, buffer, len); @@ -443,6 +447,7 @@ static int create_card(struct usb_device *usb_dev, cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); spin_lock_init(&cdev->spinlock); + mutex_init(&cdev->ep1_out_mutex); *cardp = card; return 0; diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index 1c6f34693fa8..822d872c8ad1 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -2,6 +2,8 @@ #ifndef CAIAQ_DEVICE_H #define CAIAQ_DEVICE_H +#include + #include "../usbaudio.h" #define USB_VID_NATIVEINSTRUMENTS 0x17cc @@ -68,6 +70,7 @@ struct snd_usb_caiaqdev { unsigned char ep1_in_buf[EP1_BUFSIZE]; unsigned char ep1_out_buf[EP1_BUFSIZE]; + struct mutex ep1_out_mutex; /* protects ep1_out_buf */ unsigned char midi_out_buf[EP1_BUFSIZE]; struct caiaq_device_spec spec;