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: Jaroslav Kysela <perex@perex.cz>
Signed-off-by: Curtis Malainey <cujomalainey@chromium.org>
Tested-by: Curtis Malainey <cujomalainey@chromium.org>
Link: https://lore.kernel.org/r/20230816160252.23396-3-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
 
                                                                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 */
 
        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);
        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);
 }
 
 /*
        xa_destroy(&card->ctl_hash);
 #endif
        up_write(&card->controls_rwsem);
-       put_device(&card->ctl_dev);
+       put_device(card->ctl_dev);
        return 0;
 }
 
        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;
 }
 
 
                        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);
                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;
 
 
 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;