| From 549f8ffc7b2f7561bea7f90930b6c5104318e87b Mon Sep 17 00:00:00 2001 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Wed, 26 Jan 2022 15:50:11 +0100 |
| Subject: ALSA: hda: Fix UAF of leds class devs at unbinding |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit 549f8ffc7b2f7561bea7f90930b6c5104318e87b upstream. |
| |
| The LED class devices that are created by HD-audio codec drivers are |
| registered via devm_led_classdev_register() and associated with the |
| HD-audio codec device. Unfortunately, it turned out that the devres |
| release doesn't work for this case; namely, since the codec resource |
| release happens before the devm call chain, it triggers a NULL |
| dereference or a UAF for a stale set_brightness_delay callback. |
| |
| For fixing the bug, this patch changes the LED class device register |
| and unregister in a manual manner without devres, keeping the |
| instances in hda_gen_spec. |
| |
| Reported-by: Alexander Sergeyev <sergeev917@gmail.com> |
| Cc: <stable@vger.kernel.org> |
| Link: https://lore.kernel.org/r/20220111195229.a77wrpjclqwrx4bx@localhost.localdomain |
| Link: https://lore.kernel.org/r/20220126145011.16728-1-tiwai@suse.de |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| sound/pci/hda/hda_generic.c | 17 +++++++++++++++-- |
| sound/pci/hda/hda_generic.h | 3 +++ |
| 2 files changed, 18 insertions(+), 2 deletions(-) |
| |
| --- a/sound/pci/hda/hda_generic.c |
| +++ b/sound/pci/hda/hda_generic.c |
| @@ -91,6 +91,12 @@ static void snd_hda_gen_spec_free(struct |
| free_kctls(spec); |
| snd_array_free(&spec->paths); |
| snd_array_free(&spec->loopback_list); |
| +#ifdef CONFIG_SND_HDA_GENERIC_LEDS |
| + if (spec->led_cdevs[LED_AUDIO_MUTE]) |
| + led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]); |
| + if (spec->led_cdevs[LED_AUDIO_MICMUTE]) |
| + led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]); |
| +#endif |
| } |
| |
| /* |
| @@ -3911,7 +3917,10 @@ static int create_mute_led_cdev(struct h |
| enum led_brightness), |
| bool micmute) |
| { |
| + struct hda_gen_spec *spec = codec->spec; |
| struct led_classdev *cdev; |
| + int idx = micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE; |
| + int err; |
| |
| cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL); |
| if (!cdev) |
| @@ -3921,10 +3930,14 @@ static int create_mute_led_cdev(struct h |
| cdev->max_brightness = 1; |
| cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute"; |
| cdev->brightness_set_blocking = callback; |
| - cdev->brightness = ledtrig_audio_get(micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE); |
| + cdev->brightness = ledtrig_audio_get(idx); |
| cdev->flags = LED_CORE_SUSPENDRESUME; |
| |
| - return devm_led_classdev_register(&codec->core.dev, cdev); |
| + err = led_classdev_register(&codec->core.dev, cdev); |
| + if (err < 0) |
| + return err; |
| + spec->led_cdevs[idx] = cdev; |
| + return 0; |
| } |
| |
| static void vmaster_update_mute_led(void *private_data, int enabled) |
| --- a/sound/pci/hda/hda_generic.h |
| +++ b/sound/pci/hda/hda_generic.h |
| @@ -305,6 +305,9 @@ struct hda_gen_spec { |
| struct hda_jack_callback *cb); |
| void (*mic_autoswitch_hook)(struct hda_codec *codec, |
| struct hda_jack_callback *cb); |
| + |
| + /* leds */ |
| + struct led_classdev *led_cdevs[NUM_AUDIO_LEDS]; |
| }; |
| |
| /* values for add_stereo_mix_input flag */ |