* [PATCH v2] ALSA: usx2y: Release the URBs when the DSP load fails
2026-10-31 16:00 ` Takashi Iwai
@ 2026-10-31 17:16 ` YuanZhe Liu
2026-10-31 17:27 ` sashiko-bot
2026-10-31 18:56 ` [PATCH v3] " YuanZhe Liu
2026-10-31 19:15 ` [PATCH v4] ALSA: usx2y: Refuse a retried DSP load after a failure YuanZhe Liu
2 siblings, 1 reply; 11+ messages in thread
From: YuanZhe Liu @ 2026-10-31 17:16 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: Bin Yu, Cheng Wen, linux-sound, linux-kernel, stable
snd_usx2y_hwdep_dsp_load() sets up the asynchronous sequence URBs and
the interrupt URB when the FPGA firmware image with index 1 is loaded,
and then creates the ALSA devices. If usx2y_in04_init() or
usx2y_create_alsa_devices() fails, the URBs that were already set up
are left in place.
The core keeps the index unloaded when the callback fails, so user
space may issue the same request again. The second attempt then enters
usx2y_async_seq04_init() or usx2y_in04_init() with the URBs still
allocated and trips WARN_ON(usx2y->as04.buffer) or
WARN_ON(usx2y->in04_urb).
Release the URBs on these error paths instead. A retried request then
starts from a clean state and the assertions keep their meaning.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Co-developed-by: Bin Yu <byu@xidian.edu.cn>
Signed-off-by: Bin Yu <byu@xidian.edu.cn>
Co-developed-by: Cheng Wen <wencheng@xidian.edu.cn>
Signed-off-by: Cheng Wen <wencheng@xidian.edu.cn>
Signed-off-by: YuanZhe Liu <25031212351@stu.xidian.edu.cn>
---
v2: release the URBs on the failing paths of snd_usx2y_hwdep_dsp_load()
instead of removing the WARN_ON()s, as suggested by Takashi Iwai.
sound/usb/usx2y/usX2Yhwdep.c | 2 ++
sound/usb/usx2y/usbusx2y.c | 16 ++++++++++++++++
sound/usb/usx2y/usbusx2y.h | 1 +
3 files changed, 19 insertions(+)
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 7f153a2fcdd8..13995e81d2ae 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -201,11 +201,13 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
err = usx2y_in04_init(priv);
if (err) {
dev_err(&dev->dev, "usx2y_in04_init error\n");
+ usx2y_free_urbs(priv);
return err;
}
err = usx2y_create_alsa_devices(hw->card);
if (err) {
dev_err(&dev->dev, "usx2y_create_alsa_devices error %i\n", err);
+ usx2y_free_urbs(priv);
return err;
}
priv->chip_status |= USX2Y_STAT_CHIP_INIT;
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index 108f9bddf9aa..8a551abe47dc 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -349,6 +349,22 @@ static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s)
s->buffer = NULL;
}
+/*
+ * Release the URBs set up by usx2y_async_seq04_init() and usx2y_in04_init().
+ * Used when a later step of the DSP loading fails.
+ */
+void usx2y_free_urbs(struct usx2ydev *usx2y)
+{
+ if (usx2y->in04_urb) {
+ usb_kill_urb(usx2y->in04_urb);
+ usb_free_urb(usx2y->in04_urb);
+ usx2y->in04_urb = NULL;
+ kfree(usx2y->in04_buf);
+ usx2y->in04_buf = NULL;
+ }
+ usx2y_unlinkseq(&usx2y->as04);
+}
+
static const struct usb_device_id snd_usx2y_usb_id_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h
index 7b6deed17d4b..e5e26ac8dc04 100644
--- a/sound/usb/usx2y/usbusx2y.h
+++ b/sound/usb/usx2y/usbusx2y.h
@@ -110,6 +110,7 @@ int usx2y_audio_create(struct snd_card *card);
int usx2y_async_seq04_init(struct usx2ydev *usx2y);
int usx2y_in04_init(struct usx2ydev *usx2y);
+void usx2y_free_urbs(struct usx2ydev *usx2y);
#define NAME_ALLCAPS "US-X2Y"
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2] ALSA: usx2y: Release the URBs when the DSP load fails
2026-10-31 17:16 ` [PATCH v2] ALSA: usx2y: Release the URBs when the DSP load fails YuanZhe Liu
@ 2026-10-31 17:27 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-31 17:27 UTC (permalink / raw)
To: YuanZhe Liu; +Cc: linux-sound
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] ALSA: usx2y: missing device rollback in snd_usx2y_hwdep_dsp_load()
- [High] ALSA: usx2y: unsynchronized URB free in usx2y_free_urbs()
--
commit 8f4b5a194c16ab573b5c1441a177381d9389e8b1
Author: YuanZhe Liu <25031212351@stu.xidian.edu.cn>
ALSA: usx2y: Release the URBs when the DSP load fails
This commit adds usx2y_free_urbs() and calls it on the error paths of
snd_usx2y_hwdep_dsp_load() when usx2y_in04_init() or
usx2y_create_alsa_devices() fails, so a retried DSP load request does not
trip WARN_ON() on already allocated URBs.
> diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
> index 7f153a2fcdd81..13995e81d2ae9 100644
> --- a/sound/usb/usx2y/usX2Yhwdep.c
> +++ b/sound/usb/usx2y/usX2Yhwdep.c
> @@ -201,11 +201,13 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
> err = usx2y_in04_init(priv);
> if (err) {
> dev_err(&dev->dev, "usx2y_in04_init error\n");
> + usx2y_free_urbs(priv);
> return err;
> }
> err = usx2y_create_alsa_devices(hw->card);
> if (err) {
> dev_err(&dev->dev, "usx2y_create_alsa_devices error %i\n", err);
> + usx2y_free_urbs(priv);
> return err;
> }
[Severity: Critical]
When usx2y_create_alsa_devices() fails halfway through (for example, if
usx2y_rate_set() times out in usx2y_audio_create(), or if
usx2y_hwdep_pcm_new() or snd_card_register() fails), the MIDI and PCM
devices created earlier in usx2y_create_alsa_devices() are not rolled back:
sound/usb/usx2y/usX2Yhwdep.c:usx2y_create_alsa_devices() {
err = usx2y_create_usbmidi(card);
if (err < 0) {
...
return err;
}
err = usx2y_audio_create(card);
if (err < 0)
return err;
err = usx2y_hwdep_pcm_new(card);
if (err < 0)
return err;
err = snd_card_register(card);
if (err < 0)
return err;
}
Previously, priv->as04.buffer remained non-NULL, which blocked any retry of
SNDRV_HWDEP_IOCTL_DSP_LOAD at usx2y_async_seq04_init() with -EBUSY. With
usx2y_free_urbs(priv) clearing priv->as04.buffer and priv->in04_urb here, a
retried DSP load for index 1 will now re-enter usx2y_create_alsa_devices()
on the uncleaned card state.
In usx2y_audio_create() -> usx2y_audio_stream_new(), each call indexes into
usx2y(card)->subs[4] using 2 * usx2y(card)->pcm_devs and increments
pcm_devs:
sound/usb/usx2y/usbusx2yaudio.c:usx2y_audio_stream_new() {
struct snd_usx2y_substream **usx2y_substream =
usx2y(card)->subs + 2 * usx2y(card)->pcm_devs;
for (i = playback_endpoint ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
i <= SNDRV_PCM_STREAM_CAPTURE; ++i) {
usx2y_substream[i] = kzalloc_obj(struct snd_usx2y_substream);
...
}
...
usx2y(card)->pcm_devs++;
}
Since struct usx2ydev only has 4 elements in subs[4], won't retrying
SNDRV_HWDEP_IOCTL_DSP_LOAD once pcm_devs reaches 2 (on the first retry for
US-428, or the second retry for US-122/US-224) write out of bounds past
usx2y->subs[4] into prepare_subs, prepare_wait_queue, midi_list, and
pcm_devs?
In addition, if snd_card_register() fails after snd_device_register_all()
has already registered the SNDRV_DEV_PCM devices (which also happens on
retry when the duplicate rawmidi device 0 fails registration with -EBUSY),
those PCM device nodes remain exposed to userspace while usx2y_free_urbs()
sets usx2y->in04_urb to NULL. If userspace opens the PCM device and invokes
SNDRV_PCM_IOCTL_PREPARE, won't snd_usx2y_pcm_prepare() ->
usx2y_format_set() dereference a NULL usx2y->in04_urb at:
usb_kill_urb(usx2y->in04_urb);
...
usx2y->in04_urb->dev = usx2y->dev;
err = usb_submit_urb(usx2y->in04_urb, GFP_KERNEL);
> diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
> index 108f9bddf9aa3..8a551abe47dc6 100644
> --- a/sound/usb/usx2y/usbusx2y.c
> +++ b/sound/usb/usx2y/usbusx2y.c
> @@ -349,6 +349,22 @@ static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s)
> s->buffer = NULL;
> }
>
> +/*
> + * Release the URBs set up by usx2y_async_seq04_init() and usx2y_in04_init().
> + * Used when a later step of the DSP loading fails.
> + */
> +void usx2y_free_urbs(struct usx2ydev *usx2y)
> +{
> + if (usx2y->in04_urb) {
> + usb_kill_urb(usx2y->in04_urb);
> + usb_free_urb(usx2y->in04_urb);
> + usx2y->in04_urb = NULL;
> + kfree(usx2y->in04_buf);
> + usx2y->in04_buf = NULL;
> + }
> + usx2y_unlinkseq(&usx2y->as04);
> +}
[Severity: High]
Can usx2y_free_urbs() race with snd_usx2y_disconnect() or concurrent
SNDRV_HWDEP_IOCTL_DSP_LOAD calls?
snd_hwdep_ioctl() invokes snd_hwdep_dsp_load() ->
snd_usx2y_hwdep_dsp_load() without holding a driver lock, and
snd_usx2y_hwdep_dsp_load() sleeps in msleep(250), usx2y_in04_init(), and
usx2y_create_alsa_devices(). If the USB device disconnects during DSP
loading, the initialization step fails and calls usx2y_free_urbs(priv) at
the same time as the USB hub thread runs snd_usx2y_disconnect():
sound/usb/usx2y/usbusx2y.c:snd_usx2y_disconnect() {
...
usx2y_unlinkseq(&usx2y->as04);
usb_kill_urb(usx2y->in04_urb);
snd_card_disconnect(card);
...
}
Since neither path is serialized, couldn't snd_usx2y_disconnect() call
usb_kill_urb(usx2y->in04_urb) while or after usx2y_free_urbs() calls
usb_free_urb(usx2y->in04_urb), and couldn't both paths enter
usx2y_unlinkseq(&usx2y->as04) concurrently (or two threads sharing the
hwdep fd race in SNDRV_HWDEP_IOCTL_DSP_LOAD), resulting in a use-after-free
or double-free of in04_urb, as04.urb[i], and as04.buffer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031171612.8148-1-25031212351@stu.xidian.edu.cn?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3] ALSA: usx2y: Release the URBs when the DSP load fails
2026-10-31 16:00 ` Takashi Iwai
2026-10-31 17:16 ` [PATCH v2] ALSA: usx2y: Release the URBs when the DSP load fails YuanZhe Liu
@ 2026-10-31 18:56 ` YuanZhe Liu
2026-10-31 19:07 ` sashiko-bot
2026-10-31 19:15 ` [PATCH v4] ALSA: usx2y: Refuse a retried DSP load after a failure YuanZhe Liu
2 siblings, 1 reply; 11+ messages in thread
From: YuanZhe Liu @ 2026-10-31 18:56 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: Bin Yu, Cheng Wen, linux-sound, linux-kernel, stable
snd_usx2y_hwdep_dsp_load() sets up the asynchronous sequence URBs and
the interrupt URB when the FPGA firmware image with index 1 is loaded,
and then creates the ALSA devices. If usx2y_in04_init() or
usx2y_create_alsa_devices() fails, the URBs that were already set up
are left in place.
The core keeps the index unloaded when the callback fails, so user
space may issue the same request again. The second attempt then enters
usx2y_async_seq04_init() or usx2y_in04_init() with the URBs still
allocated and trips WARN_ON(usx2y->as04.buffer) or
WARN_ON(usx2y->in04_urb).
Release the URBs on these error paths instead, so that a retried
request starts from a clean state and the assertions keep their
meaning.
Releasing the URBs also lets a retried request reach
usx2y_create_alsa_devices() again, on a card that keeps the MIDI and
PCM devices created by the failed attempt. For the US-428 the audio
streams would then index usx2y->subs[] past its end, so do not
initialise a partially created card a second time: mark the chip as
hung when the ALSA devices could not be created, and refuse further DSP
load requests. A failure of usx2y_in04_init() leaves the card
untouched and remains retryable.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Co-developed-by: Bin Yu <byu@xidian.edu.cn>
Signed-off-by: Bin Yu <byu@xidian.edu.cn>
Co-developed-by: Cheng Wen <wencheng@xidian.edu.cn>
Signed-off-by: Cheng Wen <wencheng@xidian.edu.cn>
Signed-off-by: YuanZhe Liu <25031212351@stu.xidian.edu.cn>
---
v3: do not initialise a partially created card a second time. A failed
usx2y_create_alsa_devices() now marks the chip as hung, so a retried
DSP load request returns -ENODEV instead of running the ALSA device
creation again.
sound/usb/usx2y/usX2Yhwdep.c | 6 ++++++
sound/usb/usx2y/usbusx2y.c | 16 ++++++++++++++++
sound/usb/usx2y/usbusx2y.h | 1 +
3 files changed, 23 insertions(+)
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 7f153a2fcdd8..366167c62575 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -179,6 +179,9 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
int lret, err;
char *buf;
+ if (priv->chip_status & USX2Y_STAT_CHIP_HUP)
+ return -ENODEV;
+
buf = memdup_user(dsp->image, dsp->length);
if (IS_ERR(buf))
return PTR_ERR(buf);
@@ -201,11 +204,14 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
err = usx2y_in04_init(priv);
if (err) {
dev_err(&dev->dev, "usx2y_in04_init error\n");
+ usx2y_free_urbs(priv);
return err;
}
err = usx2y_create_alsa_devices(hw->card);
if (err) {
dev_err(&dev->dev, "usx2y_create_alsa_devices error %i\n", err);
+ usx2y_free_urbs(priv);
+ priv->chip_status |= USX2Y_STAT_CHIP_HUP;
return err;
}
priv->chip_status |= USX2Y_STAT_CHIP_INIT;
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index 108f9bddf9aa..8a551abe47dc 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -349,6 +349,22 @@ static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s)
s->buffer = NULL;
}
+/*
+ * Release the URBs set up by usx2y_async_seq04_init() and usx2y_in04_init().
+ * Used when a later step of the DSP loading fails.
+ */
+void usx2y_free_urbs(struct usx2ydev *usx2y)
+{
+ if (usx2y->in04_urb) {
+ usb_kill_urb(usx2y->in04_urb);
+ usb_free_urb(usx2y->in04_urb);
+ usx2y->in04_urb = NULL;
+ kfree(usx2y->in04_buf);
+ usx2y->in04_buf = NULL;
+ }
+ usx2y_unlinkseq(&usx2y->as04);
+}
+
static const struct usb_device_id snd_usx2y_usb_id_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h
index 7b6deed17d4b..e5e26ac8dc04 100644
--- a/sound/usb/usx2y/usbusx2y.h
+++ b/sound/usb/usx2y/usbusx2y.h
@@ -110,6 +110,7 @@ int usx2y_audio_create(struct snd_card *card);
int usx2y_async_seq04_init(struct usx2ydev *usx2y);
int usx2y_in04_init(struct usx2ydev *usx2y);
+void usx2y_free_urbs(struct usx2ydev *usx2y);
#define NAME_ALLCAPS "US-X2Y"
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v3] ALSA: usx2y: Release the URBs when the DSP load fails
2026-10-31 18:56 ` [PATCH v3] " YuanZhe Liu
@ 2026-10-31 19:07 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-31 19:07 UTC (permalink / raw)
To: YuanZhe Liu; +Cc: linux-sound
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] ALSA: usx2y: NULL deref of in04_urb after usx2y_create_alsa_devices() fails
- [High] ALSA: usx2y: race between usx2y_free_urbs() and snd_usx2y_disconnect()
--
commit 45a7e14f54ac22210b87cf753196e744d24a4e6a
Author: YuanZhe Liu <25031212351@stu.xidian.edu.cn>
ALSA: usx2y: Release the URBs when the DSP load fails
This commit adds usx2y_free_urbs() to release the asynchronous sequence URBs
and the interrupt URB when usx2y_in04_init() or usx2y_create_alsa_devices()
fails in snd_usx2y_hwdep_dsp_load(). It also sets USX2Y_STAT_CHIP_HUP if
usx2y_create_alsa_devices() fails so that subsequent DSP load requests are
refused with -ENODEV.
> diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
> index 7f153a2fcdd81..366167c62575d 100644
> --- a/sound/usb/usx2y/usX2Yhwdep.c
> +++ b/sound/usb/usx2y/usX2Yhwdep.c
[ ... ]
> @@ -201,11 +204,14 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
> err = usx2y_in04_init(priv);
> if (err) {
> dev_err(&dev->dev, "usx2y_in04_init error\n");
> + usx2y_free_urbs(priv);
> return err;
> }
> err = usx2y_create_alsa_devices(hw->card);
> if (err) {
> dev_err(&dev->dev, "usx2y_create_alsa_devices error %i\n", err);
> + usx2y_free_urbs(priv);
> + priv->chip_status |= USX2Y_STAT_CHIP_HUP;
> return err;
> }
[Severity: High]
As reported by Sashiko on v2 [1], can freeing priv->in04_urb here cause a
null pointer dereference in usx2y_format_set() if snd_card_register() fails
after registering the PCM devices?
In sound/usb/usx2y/usX2Yhwdep.c:usx2y_create_alsa_devices(), the driver
creates the rawmidi, PCM, and hwdep PCM devices and then calls
snd_card_register(card):
err = usx2y_create_usbmidi(card);
if (err < 0) {
...
return err;
}
err = usx2y_audio_create(card);
if (err < 0)
return err;
err = usx2y_hwdep_pcm_new(card);
if (err < 0)
return err;
err = snd_card_register(card);
if (err < 0)
return err;
Because snd_device_new() orders card->devices by enum snd_device_type,
SNDRV_DEV_PCM precedes SNDRV_DEV_RAWMIDI and SNDRV_DEV_HWDEP. When
snd_card_register() runs snd_device_register_all(), it registers the PCM
devices before registering the rawmidi device, the second hwdep device, or the
procfs entries in snd_info_card_register(). If any of those later registration
steps fail, snd_card_register() returns an error without unregistering the
already-registered PCM device nodes.
On that failure path, snd_usx2y_hwdep_dsp_load() now calls
usx2y_free_urbs(priv), which frees priv->in04_urb and sets it to NULL, and
then sets USX2Y_STAT_CHIP_HUP. However, neither snd_usx2y_pcm_open() nor
snd_usx2y_pcm_prepare() in sound/usb/usx2y/usbusx2yaudio.c checks
USX2Y_STAT_CHIP_HUP or USX2Y_STAT_CHIP_INIT.
If user space opens the registered PCM device and issues
SNDRV_PCM_IOCTL_PREPARE, snd_usx2y_pcm_prepare() calls usx2y_format_set(),
which unconditionally dereferences usx2y->in04_urb:
usx2y->in04_urb->dev = usx2y->dev;
err = usb_submit_urb(usx2y->in04_urb, GFP_KERNEL);
> diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
> index 108f9bddf9aa3..8a551abe47dc6 100644
> --- a/sound/usb/usx2y/usbusx2y.c
> +++ b/sound/usb/usx2y/usbusx2y.c
> @@ -349,6 +349,22 @@ static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s)
> s->buffer = NULL;
> }
>
> +/*
> + * Release the URBs set up by usx2y_async_seq04_init() and usx2y_in04_init().
> + * Used when a later step of the DSP loading fails.
> + */
> +void usx2y_free_urbs(struct usx2ydev *usx2y)
> +{
> + if (usx2y->in04_urb) {
> + usb_kill_urb(usx2y->in04_urb);
> + usb_free_urb(usx2y->in04_urb);
> + usx2y->in04_urb = NULL;
> + kfree(usx2y->in04_buf);
> + usx2y->in04_buf = NULL;
> + }
> + usx2y_unlinkseq(&usx2y->as04);
> +}
[Severity: High]
As reported by Sashiko on v2 [2], does usx2y_free_urbs() race with
snd_usx2y_disconnect() when the USB device disconnects during DSP loading?
When dsp->index == 1 in snd_usx2y_hwdep_dsp_load(), if the USB device is
disconnected while usx2y_in04_init() or usx2y_create_alsa_devices() is
running (for example during the wait_event_timeout() in usx2y_rate_set()),
that initialization fails and calls usx2y_free_urbs(priv) under hw->dsp_mutex.
At the same time, the USB disconnect handler runs
sound/usb/usx2y/usbusx2y.c:snd_usx2y_disconnect() without holding
hw->dsp_mutex:
usx2y->chip_status = USX2Y_STAT_CHIP_HUP;
usx2y_unlinkseq(&usx2y->as04);
usb_kill_urb(usx2y->in04_urb);
Previously, usx2y->in04_urb was only freed later in
snd_usx2y_card_private_free() after snd_usx2y_disconnect() had finished. With
usx2y_free_urbs() freeing usx2y->in04_urb before clearing the pointer, can
snd_usx2y_disconnect() pass a freed usx2y->in04_urb to usb_kill_urb()?
Additionally, both usx2y_free_urbs() and snd_usx2y_disconnect() call
usx2y_unlinkseq(&usx2y->as04) concurrently:
static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s)
{
int i;
for (i = 0; i < URBS_ASYNC_SEQ; ++i) {
if (!s->urb[i])
continue;
usb_kill_urb(s->urb[i]);
usb_free_urb(s->urb[i]);
s->urb[i] = NULL;
}
kfree(s->buffer);
s->buffer = NULL;
}
Since usb_kill_urb() sleeps and s->urb[i] and s->buffer are only set to NULL
after usb_free_urb() and kfree(), can concurrent execution of
usx2y_unlinkseq() cause a use-after-free and double-free on s->urb[i] and
s->buffer?
[1] https://lore.kernel.org/r/20261031171612.8148-1-25031212351@stu.xidian.edu.cn
[2] https://lore.kernel.org/r/20261031171612.8148-1-25031212351@stu.xidian.edu.cn
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031185619.18601-1-25031212351@stu.xidian.edu.cn?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4] ALSA: usx2y: Refuse a retried DSP load after a failure
2026-10-31 16:00 ` Takashi Iwai
2026-10-31 17:16 ` [PATCH v2] ALSA: usx2y: Release the URBs when the DSP load fails YuanZhe Liu
2026-10-31 18:56 ` [PATCH v3] " YuanZhe Liu
@ 2026-10-31 19:15 ` YuanZhe Liu
2 siblings, 0 replies; 11+ messages in thread
From: YuanZhe Liu @ 2026-10-31 19:15 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: Bin Yu, Cheng Wen, linux-sound, linux-kernel, stable
snd_usx2y_hwdep_dsp_load() sets up the asynchronous sequence URBs and
the interrupt URB when the FPGA firmware image with index 1 is loaded,
and then creates the ALSA devices. If usx2y_in04_init() or
usx2y_create_alsa_devices() fails, the URBs that were already set up
are left in place.
The core keeps the index unloaded when the callback fails, so user
space may issue the same request again. The second attempt then enters
usx2y_async_seq04_init() or usx2y_in04_init() with the URBs still
allocated and trips WARN_ON(usx2y->as04.buffer) or
WARN_ON(usx2y->in04_urb).
The URBs cannot be released at this point: the card may already have
registered PCM devices that user space can still open, and the
disconnect handler accesses the URBs without holding the DSP lock.
Mark the chip as hung instead, so that a retried request is refused
with -ENODEV and the assertions keep their meaning. The URBs are
released by snd_usx2y_card_private_free() when the card is freed.
A failing usx2y_async_seq04_init() still releases its own URBs, so that
path remains retryable.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Co-developed-by: Bin Yu <byu@xidian.edu.cn>
Signed-off-by: Bin Yu <byu@xidian.edu.cn>
Co-developed-by: Cheng Wen <wencheng@xidian.edu.cn>
Signed-off-by: Cheng Wen <wencheng@xidian.edu.cn>
Signed-off-by: YuanZhe Liu <25031212351@stu.xidian.edu.cn>
---
v4: drop the release of the URBs on the error paths. The card may already
have registered PCM devices, and the disconnect handler accesses the
URBs without the DSP lock; mark the chip as hung instead and let
snd_usx2y_card_private_free() release the URBs.
sound/usb/usx2y/usX2Yhwdep.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 7f153a2fcdd8..3800c5b5887c 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -179,6 +179,9 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
int lret, err;
char *buf;
+ if (priv->chip_status & USX2Y_STAT_CHIP_HUP)
+ return -ENODEV;
+
buf = memdup_user(dsp->image, dsp->length);
if (IS_ERR(buf))
return PTR_ERR(buf);
@@ -201,11 +204,13 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
err = usx2y_in04_init(priv);
if (err) {
dev_err(&dev->dev, "usx2y_in04_init error\n");
+ priv->chip_status |= USX2Y_STAT_CHIP_HUP;
return err;
}
err = usx2y_create_alsa_devices(hw->card);
if (err) {
dev_err(&dev->dev, "usx2y_create_alsa_devices error %i\n", err);
+ priv->chip_status |= USX2Y_STAT_CHIP_HUP;
return err;
}
priv->chip_status |= USX2Y_STAT_CHIP_INIT;
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread