]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
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>
Thu, 9 Jun 2022 08:20:51 +0000 (10:20 +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 9eb2b5ec1ec4104199c5716e2875a90af5205d31..78f3619f3de947993c95950f78499b1cee5d2418 100644 (file)
@@ -62,6 +62,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 dc2e06ae2414960beec0a04a1c0042e13407440f..45e28db6ea38da690cbf20504302392703a847c1 100644 (file)
@@ -34,8 +34,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. */
@@ -44,6 +47,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;
 }
@@ -82,8 +86,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;
 
@@ -108,6 +115,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 */
@@ -228,9 +236,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();
@@ -248,8 +258,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)
@@ -289,10 +299,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);
 
@@ -340,6 +354,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
@@ -359,8 +375,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;
@@ -379,6 +398,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);