Commit 6a66b01d authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: control: Don't embed ctl_dev

Embedding the ctl_dev in the snd_card object may result in UAF when
the delayed kobj release is used; at the delayed kobj release, it
still accesses the struct device itself while the card memory (that
embeds the struct device) may be already gone.

As a workaround, detach the struct device from the card object by
allocating via the new snd_device_alloc() helper.  The rest are just
replacing ctl_dev access to the pointer.

This is based on the fix Curtis posted initially.  In this patch, the
changes are split and use the new helper function instead.

Link: https://lore.kernel.org/r/20230801171928.1460120-1-cujomalainey@chromium.org


Reviewed-by: default avatarJaroslav Kysela <perex@perex.cz>
Signed-off-by: default avatarCurtis Malainey <cujomalainey@chromium.org>
Tested-by: default avatarCurtis Malainey <cujomalainey@chromium.org>
Link: https://lore.kernel.org/r/20230816160252.23396-3-tiwai@suse.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 7f018db1
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -96,7 +96,7 @@ struct snd_card {
								private data */
	struct list_head devices;	/* devices */

	struct device ctl_dev;		/* control device */
	struct device *ctl_dev;		/* control device */
	unsigned int last_numid;	/* last used numeric ID */
	struct rw_semaphore controls_rwsem;	/* controls lock (list and values) */
	rwlock_t ctl_files_rwlock;	/* ctl_files list lock */
+8 −6
Original line number Diff line number Diff line
@@ -2389,7 +2389,7 @@ static int snd_ctl_dev_register(struct snd_device *device)
	int err;

	err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
				  &snd_ctl_f_ops, card, &card->ctl_dev);
				  &snd_ctl_f_ops, card, card->ctl_dev);
	if (err < 0)
		return err;
	down_read(&card->controls_rwsem);
@@ -2425,7 +2425,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
	up_read(&snd_ctl_layer_rwsem);
	up_read(&card->controls_rwsem);

	return snd_unregister_device(&card->ctl_dev);
	return snd_unregister_device(card->ctl_dev);
}

/*
@@ -2447,7 +2447,7 @@ static int snd_ctl_dev_free(struct snd_device *device)
	xa_destroy(&card->ctl_hash);
#endif
	up_write(&card->controls_rwsem);
	put_device(&card->ctl_dev);
	put_device(card->ctl_dev);
	return 0;
}

@@ -2469,12 +2469,14 @@ int snd_ctl_create(struct snd_card *card)
	if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
		return -ENXIO;

	snd_device_initialize(&card->ctl_dev, card);
	dev_set_name(&card->ctl_dev, "controlC%d", card->number);
	err = snd_device_alloc(&card->ctl_dev, card);
	if (err < 0)
		return err;
	dev_set_name(card->ctl_dev, "controlC%d", card->number);

	err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
	if (err < 0)
		put_device(&card->ctl_dev);
		put_device(card->ctl_dev);
	return err;
}

+2 −2
Original line number Diff line number Diff line
@@ -688,7 +688,7 @@ static void snd_ctl_led_sysfs_add(struct snd_card *card)
			goto cerr;
		led->cards[card->number] = led_card;
		snprintf(link_name, sizeof(link_name), "led-%s", led->name);
		WARN(sysfs_create_link(&card->ctl_dev.kobj, &led_card->dev.kobj, link_name),
		WARN(sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, link_name),
			"can't create symlink to controlC%i device\n", card->number);
		WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"),
			"can't create symlink to card%i\n", card->number);
@@ -714,7 +714,7 @@ static void snd_ctl_led_sysfs_remove(struct snd_card *card)
		if (!led_card)
			continue;
		snprintf(link_name, sizeof(link_name), "led-%s", led->name);
		sysfs_remove_link(&card->ctl_dev.kobj, link_name);
		sysfs_remove_link(&card->ctl_dev->kobj, link_name);
		sysfs_remove_link(&led_card->dev.kobj, "card");
		device_unregister(&led_card->dev);
		led->cards[card->number] = NULL;
+1 −1
Original line number Diff line number Diff line
@@ -163,7 +163,7 @@ void snd_media_stop_pipeline(struct snd_usb_substream *subs)

static int snd_media_mixer_init(struct snd_usb_audio *chip)
{
	struct device *ctl_dev = &chip->card->ctl_dev;
	struct device *ctl_dev = chip->card->ctl_dev;
	struct media_intf_devnode *ctl_intf;
	struct usb_mixer_interface *mixer;
	struct media_device *mdev = chip->media_dev;