| From f3db2e2feafd8c0699fbd2d33c27ed5ea951b9d2 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 12 Apr 2022 11:16:28 +0200 |
| Subject: ALSA: jack: Access input_dev under mutex |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com> |
| |
| [ Upstream commit 1b6a6fc5280e97559287b61eade2d4b363e836f2 ] |
| |
| It is possible when using ASoC that input_dev is unregistered while |
| calling snd_jack_report, which causes NULL pointer dereference. |
| In order to prevent this serialize access to input_dev using mutex lock. |
| |
| Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com> |
| Reviewed-by: Cezary Rojewski <cezary.rojewski@intel.com> |
| Link: https://lore.kernel.org/r/20220412091628.3056922-1-amadeuszx.slawinski@linux.intel.com |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| include/sound/jack.h | 1 + |
| sound/core/jack.c | 34 +++++++++++++++++++++++++++------- |
| 2 files changed, 28 insertions(+), 7 deletions(-) |
| |
| diff --git a/include/sound/jack.h b/include/sound/jack.h |
| index 1e84bfb553cf..4742f842b457 100644 |
| --- a/include/sound/jack.h |
| +++ b/include/sound/jack.h |
| @@ -77,6 +77,7 @@ struct snd_jack { |
| const char *id; |
| #ifdef CONFIG_SND_JACK_INPUT_DEV |
| struct input_dev *input_dev; |
| + struct mutex input_dev_lock; |
| int registered; |
| int type; |
| char name[100]; |
| diff --git a/sound/core/jack.c b/sound/core/jack.c |
| index 36cfe1c54109..d2f9a92453f2 100644 |
| --- a/sound/core/jack.c |
| +++ b/sound/core/jack.c |
| @@ -48,8 +48,11 @@ static int snd_jack_dev_disconnect(struct snd_device *device) |
| #ifdef CONFIG_SND_JACK_INPUT_DEV |
| struct snd_jack *jack = device->device_data; |
| |
| - if (!jack->input_dev) |
| + mutex_lock(&jack->input_dev_lock); |
| + if (!jack->input_dev) { |
| + mutex_unlock(&jack->input_dev_lock); |
| return 0; |
| + } |
| |
| /* If the input device is registered with the input subsystem |
| * then we need to use a different deallocator. */ |
| @@ -58,6 +61,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device) |
| else |
| input_free_device(jack->input_dev); |
| jack->input_dev = NULL; |
| + mutex_unlock(&jack->input_dev_lock); |
| #endif /* CONFIG_SND_JACK_INPUT_DEV */ |
| return 0; |
| } |
| @@ -96,8 +100,11 @@ static int snd_jack_dev_register(struct snd_device *device) |
| snprintf(jack->name, sizeof(jack->name), "%s %s", |
| card->shortname, jack->id); |
| |
| - if (!jack->input_dev) |
| + mutex_lock(&jack->input_dev_lock); |
| + if (!jack->input_dev) { |
| + mutex_unlock(&jack->input_dev_lock); |
| return 0; |
| + } |
| |
| jack->input_dev->name = jack->name; |
| |
| @@ -122,6 +129,7 @@ static int snd_jack_dev_register(struct snd_device *device) |
| if (err == 0) |
| jack->registered = 1; |
| |
| + mutex_unlock(&jack->input_dev_lock); |
| return err; |
| } |
| #endif /* CONFIG_SND_JACK_INPUT_DEV */ |
| @@ -242,9 +250,11 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, |
| return -ENOMEM; |
| } |
| |
| - /* don't creat input device for phantom jack */ |
| - if (!phantom_jack) { |
| #ifdef CONFIG_SND_JACK_INPUT_DEV |
| + mutex_init(&jack->input_dev_lock); |
| + |
| + /* don't create input device for phantom jack */ |
| + if (!phantom_jack) { |
| int i; |
| |
| jack->input_dev = input_allocate_device(); |
| @@ -262,8 +272,8 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, |
| input_set_capability(jack->input_dev, EV_SW, |
| jack_switch_types[i]); |
| |
| -#endif /* CONFIG_SND_JACK_INPUT_DEV */ |
| } |
| +#endif /* CONFIG_SND_JACK_INPUT_DEV */ |
| |
| err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); |
| if (err < 0) |
| @@ -303,10 +313,14 @@ EXPORT_SYMBOL(snd_jack_new); |
| void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) |
| { |
| WARN_ON(jack->registered); |
| - if (!jack->input_dev) |
| + mutex_lock(&jack->input_dev_lock); |
| + if (!jack->input_dev) { |
| + mutex_unlock(&jack->input_dev_lock); |
| return; |
| + } |
| |
| jack->input_dev->dev.parent = parent; |
| + mutex_unlock(&jack->input_dev_lock); |
| } |
| EXPORT_SYMBOL(snd_jack_set_parent); |
| |
| @@ -354,6 +368,8 @@ EXPORT_SYMBOL(snd_jack_set_key); |
| |
| /** |
| * snd_jack_report - Report the current status of a jack |
| + * Note: This function uses mutexes and should be called from a |
| + * context which can sleep (such as a workqueue). |
| * |
| * @jack: The jack to report status for |
| * @status: The current status of the jack |
| @@ -373,8 +389,11 @@ void snd_jack_report(struct snd_jack *jack, int status) |
| status & jack_kctl->mask_bits); |
| |
| #ifdef CONFIG_SND_JACK_INPUT_DEV |
| - if (!jack->input_dev) |
| + mutex_lock(&jack->input_dev_lock); |
| + if (!jack->input_dev) { |
| + mutex_unlock(&jack->input_dev_lock); |
| return; |
| + } |
| |
| for (i = 0; i < ARRAY_SIZE(jack->key); i++) { |
| int testbit = SND_JACK_BTN_0 >> i; |
| @@ -393,6 +412,7 @@ void snd_jack_report(struct snd_jack *jack, int status) |
| } |
| |
| input_sync(jack->input_dev); |
| + mutex_unlock(&jack->input_dev_lock); |
| #endif /* CONFIG_SND_JACK_INPUT_DEV */ |
| } |
| EXPORT_SYMBOL(snd_jack_report); |
| -- |
| 2.35.1 |
| |