ALSA: jack: Access input_dev under mutex
authorAmadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Tue, 12 Apr 2022 09:16:28 +0000 (11:16 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 14 Jun 2022 14:53:44 +0000 (16:53 +0200)
[ 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
sound/core/jack.c

index 1e84bfb553cf75017f9485a355b311460320235e..4742f842b4573156648822698810daca4d3baa67 100644 (file)
@@ -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];
index 36cfe1c54109db2cc75bdc21fa9d0c80c7614112..d2f9a92453f2fee2c6339790d82587368e8119b2 100644 (file)
@@ -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);