Commit b9c82691 authored by Berk Cem Goksel's avatar Berk Cem Goksel Committed by Takashi Iwai
Browse files

ALSA: 6fire: fix use-after-free on disconnect



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: a0810c3d ("ALSA: 6fire: Release resources at card release")
Cc: stable@vger.kernel.org
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Signed-off-by: default avatarBerk Cem Goksel <berkcgoksel@gmail.com>
Link: https://patch.msgid.link/20260410051341.1069716-1-berkcgoksel@gmail.com


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 07704bbf
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -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 @@ static int usb6fire_chip_probe(struct usb_interface *intf,
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);
		}
	}
}