| From 28abd224db4a49560b452115bca3672a20e45b2f Mon Sep 17 00:00:00 2001 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Tue, 14 Apr 2026 12:59:00 +0200 |
| Subject: ALSA: caiaq: Handle probe errors properly |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit 28abd224db4a49560b452115bca3672a20e45b2f upstream. |
| |
| The probe procedure of setup_card() in caiaq driver doesn't treat the |
| error cases gracefully, e.g. the error from snd_card_register() calls |
| snd_card_free() but continues. This would lead to a UAF for the |
| further calls like snd_usb_caiaq_control_init(), as Berk suggested in |
| another patch in the link below. |
| |
| However, the problem is not only that; in general, this function drops |
| the all error handlings (as it's a void function) although its caller |
| can propagate an error to snd_probe(), which eventually calls |
| snd_card_free() as a proper error path. That said, we should treat |
| each error case in setup_card(), and just return the error code |
| promptly, which is then handled later as a fatal error in snd_probe(). |
| |
| This patch achieves it by changing the setup_card() to return an error |
| code. Also, the superfluous snd_card_free() call is removed, too. |
| |
| Note that card->private_free can be set still safely at returning an |
| error. All called functions in card_free() have checks of the |
| unassigned resources or NULL checks. |
| |
| Fixes: 8e3cd08ed8e5 ("[ALSA] caiaq - add control API and more input features") |
| Cc: stable@vger.kernel.org |
| Link: https://lore.kernel.org/20260413034941.1131465-2-berkcgoksel@gmail.com |
| Link: https://patch.msgid.link/20260414105916.364073-1-tiwai@suse.de |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| sound/usb/caiaq/device.c | 33 ++++++++++++++++++++++++--------- |
| 1 file changed, 24 insertions(+), 9 deletions(-) |
| |
| --- a/sound/usb/caiaq/device.c |
| +++ b/sound/usb/caiaq/device.c |
| @@ -304,7 +304,7 @@ int snd_usb_caiaq_set_auto_msg(struct sn |
| tmp, sizeof(tmp)); |
| } |
| |
| -static void setup_card(struct snd_usb_caiaqdev *cdev) |
| +static int setup_card(struct snd_usb_caiaqdev *cdev) |
| { |
| int ret; |
| char val[4]; |
| @@ -339,8 +339,10 @@ static void setup_card(struct snd_usb_ca |
| snd_usb_caiaq_send_command(cdev, EP1_CMD_READ_IO, NULL, 0); |
| |
| if (!wait_event_timeout(cdev->ep1_wait_queue, |
| - cdev->control_state[0] != 0xff, HZ)) |
| - return; |
| + cdev->control_state[0] != 0xff, HZ)) { |
| + dev_err(dev, "Read timeout for control state\n"); |
| + return -EINVAL; |
| + } |
| |
| /* fix up some defaults */ |
| if ((cdev->control_state[1] != 2) || |
| @@ -361,33 +363,43 @@ static void setup_card(struct snd_usb_ca |
| cdev->spec.num_digital_audio_out + |
| cdev->spec.num_digital_audio_in > 0) { |
| ret = snd_usb_caiaq_audio_init(cdev); |
| - if (ret < 0) |
| + if (ret < 0) { |
| dev_err(dev, "Unable to set up audio system (ret=%d)\n", ret); |
| + return ret; |
| + } |
| } |
| |
| if (cdev->spec.num_midi_in + |
| cdev->spec.num_midi_out > 0) { |
| ret = snd_usb_caiaq_midi_init(cdev); |
| - if (ret < 0) |
| + if (ret < 0) { |
| dev_err(dev, "Unable to set up MIDI system (ret=%d)\n", ret); |
| + return ret; |
| + } |
| } |
| |
| #ifdef CONFIG_SND_USB_CAIAQ_INPUT |
| ret = snd_usb_caiaq_input_init(cdev); |
| - if (ret < 0) |
| + if (ret < 0) { |
| dev_err(dev, "Unable to set up input system (ret=%d)\n", ret); |
| + return ret; |
| + } |
| #endif |
| |
| /* finally, register the card and all its sub-instances */ |
| ret = snd_card_register(cdev->chip.card); |
| if (ret < 0) { |
| dev_err(dev, "snd_card_register() returned %d\n", ret); |
| - snd_card_free(cdev->chip.card); |
| + return ret; |
| } |
| |
| ret = snd_usb_caiaq_control_init(cdev); |
| - if (ret < 0) |
| + if (ret < 0) { |
| dev_err(dev, "Unable to set up control system (ret=%d)\n", ret); |
| + return ret; |
| + } |
| + |
| + return 0; |
| } |
| |
| static void card_free(struct snd_card *card) |
| @@ -513,8 +525,11 @@ static int init_card(struct snd_usb_caia |
| snprintf(card->longname, sizeof(card->longname), "%s %s (%s)", |
| cdev->vendor_name, cdev->product_name, usbpath); |
| |
| - setup_card(cdev); |
| card->private_free = card_free; |
| + err = setup_card(cdev); |
| + if (err < 0) |
| + return err; |
| + |
| return 0; |
| |
| err_kill_urb: |