Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

+12 -10
+1 -1
include/sound/core.h
··· 96 96 private data */ 97 97 struct list_head devices; /* devices */ 98 98 99 - struct device ctl_dev; /* control device */ 99 + struct device *ctl_dev; /* control device */ 100 100 unsigned int last_numid; /* last used numeric ID */ 101 101 struct rw_semaphore controls_rwsem; /* controls lock (list and values) */ 102 102 rwlock_t ctl_files_rwlock; /* ctl_files list lock */
+8 -6
sound/core/control.c
··· 2389 2389 int err; 2390 2390 2391 2391 err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, 2392 - &snd_ctl_f_ops, card, &card->ctl_dev); 2392 + &snd_ctl_f_ops, card, card->ctl_dev); 2393 2393 if (err < 0) 2394 2394 return err; 2395 2395 down_read(&card->controls_rwsem); ··· 2425 2425 up_read(&snd_ctl_layer_rwsem); 2426 2426 up_read(&card->controls_rwsem); 2427 2427 2428 - return snd_unregister_device(&card->ctl_dev); 2428 + return snd_unregister_device(card->ctl_dev); 2429 2429 } 2430 2430 2431 2431 /* ··· 2447 2447 xa_destroy(&card->ctl_hash); 2448 2448 #endif 2449 2449 up_write(&card->controls_rwsem); 2450 - put_device(&card->ctl_dev); 2450 + put_device(card->ctl_dev); 2451 2451 return 0; 2452 2452 } 2453 2453 ··· 2469 2469 if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS)) 2470 2470 return -ENXIO; 2471 2471 2472 - snd_device_initialize(&card->ctl_dev, card); 2473 - dev_set_name(&card->ctl_dev, "controlC%d", card->number); 2472 + err = snd_device_alloc(&card->ctl_dev, card); 2473 + if (err < 0) 2474 + return err; 2475 + dev_set_name(card->ctl_dev, "controlC%d", card->number); 2474 2476 2475 2477 err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); 2476 2478 if (err < 0) 2477 - put_device(&card->ctl_dev); 2479 + put_device(card->ctl_dev); 2478 2480 return err; 2479 2481 } 2480 2482
+2 -2
sound/core/control_led.c
··· 688 688 goto cerr; 689 689 led->cards[card->number] = led_card; 690 690 snprintf(link_name, sizeof(link_name), "led-%s", led->name); 691 - WARN(sysfs_create_link(&card->ctl_dev.kobj, &led_card->dev.kobj, link_name), 691 + WARN(sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, link_name), 692 692 "can't create symlink to controlC%i device\n", card->number); 693 693 WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"), 694 694 "can't create symlink to card%i\n", card->number); ··· 714 714 if (!led_card) 715 715 continue; 716 716 snprintf(link_name, sizeof(link_name), "led-%s", led->name); 717 - sysfs_remove_link(&card->ctl_dev.kobj, link_name); 717 + sysfs_remove_link(&card->ctl_dev->kobj, link_name); 718 718 sysfs_remove_link(&led_card->dev.kobj, "card"); 719 719 device_unregister(&led_card->dev); 720 720 led->cards[card->number] = NULL;
+1 -1
sound/usb/media.c
··· 163 163 164 164 static int snd_media_mixer_init(struct snd_usb_audio *chip) 165 165 { 166 - struct device *ctl_dev = &chip->card->ctl_dev; 166 + struct device *ctl_dev = chip->card->ctl_dev; 167 167 struct media_intf_devnode *ctl_intf; 168 168 struct usb_mixer_interface *mixer; 169 169 struct media_device *mdev = chip->media_dev;