]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: 6fire: fix use-after-free on disconnect
authorBerk Cem Goksel <berkcgoksel@gmail.com>
Fri, 10 Apr 2026 05:13:41 +0000 (08:13 +0300)
committerTakashi Iwai <tiwai@suse.de>
Fri, 10 Apr 2026 12:59:47 +0000 (14:59 +0200)
In usb6fire_chip_abort(), the chip struct is allocated as the card's
private data (via snd_card_new with sizeof(struct sfire_chip)).  When
snd_card_free_when_closed() is called and no file handles are open, the
card and embedded chip are freed synchronously.  The subsequent
chip->card = NULL write then hits freed slab memory.

Call trace:
  usb6fire_chip_abort sound/usb/6fire/chip.c:59 [inline]
  usb6fire_chip_disconnect+0x348/0x358 sound/usb/6fire/chip.c:182
  usb_unbind_interface+0x1a8/0x88c drivers/usb/core/driver.c:458
  ...
  hub_event+0x1a04/0x4518 drivers/usb/core/hub.c:5953

Fix by moving the card lifecycle out of usb6fire_chip_abort() and into
usb6fire_chip_disconnect().  The card pointer is saved in a local
before any teardown, snd_card_disconnect() is called first to prevent
new opens, URBs are aborted while chip is still valid, and
snd_card_free_when_closed() is called last so chip is never accessed
after the card may be freed.

Fixes: a0810c3d6dd2 ("ALSA: 6fire: Release resources at card release")
Cc: stable@vger.kernel.org
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Signed-off-by: Berk Cem Goksel <berkcgoksel@gmail.com>
Link: https://patch.msgid.link/20260410051341.1069716-1-berkcgoksel@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/6fire/chip.c

index 5ff78814e687502ecc13b5c8a051f4885c32a0f1..874f6cd503ca595ce02d5f286494f45050bd034e 100644 (file)
@@ -53,11 +53,6 @@ static void usb6fire_chip_abort(struct sfire_chip *chip)
                        usb6fire_comm_abort(chip);
                if (chip->control)
                        usb6fire_control_abort(chip);
-               if (chip->card) {
-                       snd_card_disconnect(chip->card);
-                       snd_card_free_when_closed(chip->card);
-                       chip->card = NULL;
-               }
        }
 }
 
@@ -168,6 +163,7 @@ destroy_chip:
 static void usb6fire_chip_disconnect(struct usb_interface *intf)
 {
        struct sfire_chip *chip;
+       struct snd_card *card;
 
        chip = usb_get_intfdata(intf);
        if (chip) { /* if !chip, fw upload has been performed */
@@ -178,8 +174,19 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf)
                                chips[chip->regidx] = NULL;
                        }
 
+                       /*
+                        * Save card pointer before teardown.
+                        * snd_card_free_when_closed() may free card (and
+                        * the embedded chip) immediately, so it must be
+                        * called last and chip must not be accessed after.
+                        */
+                       card = chip->card;
                        chip->shutdown = true;
+                       if (card)
+                               snd_card_disconnect(card);
                        usb6fire_chip_abort(chip);
+                       if (card)
+                               snd_card_free_when_closed(card);
                }
        }
 }