Skip to content

Commit 3ae72f6

Browse files
mudongliangtiwai
authored andcommitted
ALSA: control led: fix memory leak in snd_ctl_led_register
The snd_ctl_led_sysfs_add and snd_ctl_led_sysfs_remove should contain the refcount operations in pair. However, snd_ctl_led_sysfs_remove fails to decrease the refcount to zero, which causes device_release never to be invoked. This leads to memory leak to some resources, like struct device_private. In addition, we also free some other similar memory leaks in snd_ctl_led_init/snd_ctl_led_exit. Fix this by replacing device_del to device_unregister in snd_ctl_led_sysfs_remove/snd_ctl_led_init/snd_ctl_led_exit. Note that, when CONFIG_DEBUG_KOBJECT_RELEASE is enabled, put_device will call kobject_release and delay the release of kobject, which will cause use-after-free when the memory backing the kobject is freed at once. Reported-by: [email protected] Fixes: a135dfb ("ALSA: led control - add sysfs kcontrol LED marking layer") Signed-off-by: Dongliang Mu <[email protected]> Reviewed-by: Dan Carpenter <[email protected]> Reviewed-by: Jaroslav Kysela <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 901be14 commit 3ae72f6

File tree

1 file changed

+26
-7
lines changed

1 file changed

+26
-7
lines changed

sound/core/control_led.c

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ MODULE_LICENSE("GPL");
1717
#define MAX_LED (((SNDRV_CTL_ELEM_ACCESS_MIC_LED - SNDRV_CTL_ELEM_ACCESS_SPK_LED) \
1818
>> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) + 1)
1919

20+
#define to_led_card_dev(_dev) \
21+
container_of(_dev, struct snd_ctl_led_card, dev)
22+
2023
enum snd_ctl_led_mode {
2124
MODE_FOLLOW_MUTE = 0,
2225
MODE_FOLLOW_ROUTE,
@@ -371,6 +374,21 @@ static void snd_ctl_led_disconnect(struct snd_card *card)
371374
snd_ctl_led_refresh();
372375
}
373376

377+
static void snd_ctl_led_card_release(struct device *dev)
378+
{
379+
struct snd_ctl_led_card *led_card = to_led_card_dev(dev);
380+
381+
kfree(led_card);
382+
}
383+
384+
static void snd_ctl_led_release(struct device *dev)
385+
{
386+
}
387+
388+
static void snd_ctl_led_dev_release(struct device *dev)
389+
{
390+
}
391+
374392
/*
375393
* sysfs
376394
*/
@@ -663,6 +681,7 @@ static void snd_ctl_led_sysfs_add(struct snd_card *card)
663681
led_card->number = card->number;
664682
led_card->led = led;
665683
device_initialize(&led_card->dev);
684+
led_card->dev.release = snd_ctl_led_card_release;
666685
if (dev_set_name(&led_card->dev, "card%d", card->number) < 0)
667686
goto cerr;
668687
led_card->dev.parent = &led->dev;
@@ -681,7 +700,6 @@ static void snd_ctl_led_sysfs_add(struct snd_card *card)
681700
put_device(&led_card->dev);
682701
cerr2:
683702
printk(KERN_ERR "snd_ctl_led: unable to add card%d", card->number);
684-
kfree(led_card);
685703
}
686704
}
687705

@@ -700,8 +718,7 @@ static void snd_ctl_led_sysfs_remove(struct snd_card *card)
700718
snprintf(link_name, sizeof(link_name), "led-%s", led->name);
701719
sysfs_remove_link(&card->ctl_dev.kobj, link_name);
702720
sysfs_remove_link(&led_card->dev.kobj, "card");
703-
device_del(&led_card->dev);
704-
kfree(led_card);
721+
device_unregister(&led_card->dev);
705722
led->cards[card->number] = NULL;
706723
}
707724
}
@@ -723,6 +740,7 @@ static int __init snd_ctl_led_init(void)
723740

724741
device_initialize(&snd_ctl_led_dev);
725742
snd_ctl_led_dev.class = sound_class;
743+
snd_ctl_led_dev.release = snd_ctl_led_dev_release;
726744
dev_set_name(&snd_ctl_led_dev, "ctl-led");
727745
if (device_add(&snd_ctl_led_dev)) {
728746
put_device(&snd_ctl_led_dev);
@@ -733,15 +751,16 @@ static int __init snd_ctl_led_init(void)
733751
INIT_LIST_HEAD(&led->controls);
734752
device_initialize(&led->dev);
735753
led->dev.parent = &snd_ctl_led_dev;
754+
led->dev.release = snd_ctl_led_release;
736755
led->dev.groups = snd_ctl_led_dev_attr_groups;
737756
dev_set_name(&led->dev, led->name);
738757
if (device_add(&led->dev)) {
739758
put_device(&led->dev);
740759
for (; group > 0; group--) {
741760
led = &snd_ctl_leds[group - 1];
742-
device_del(&led->dev);
761+
device_unregister(&led->dev);
743762
}
744-
device_del(&snd_ctl_led_dev);
763+
device_unregister(&snd_ctl_led_dev);
745764
return -ENOMEM;
746765
}
747766
}
@@ -767,9 +786,9 @@ static void __exit snd_ctl_led_exit(void)
767786
}
768787
for (group = 0; group < MAX_LED; group++) {
769788
led = &snd_ctl_leds[group];
770-
device_del(&led->dev);
789+
device_unregister(&led->dev);
771790
}
772-
device_del(&snd_ctl_led_dev);
791+
device_unregister(&snd_ctl_led_dev);
773792
snd_ctl_led_clean(NULL);
774793
}
775794

0 commit comments

Comments
 (0)