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>
const char *id;
#ifdef CONFIG_SND_JACK_INPUT_DEV
struct input_dev *input_dev;
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];
int registered;
int type;
char name[100];
#ifdef CONFIG_SND_JACK_INPUT_DEV
struct snd_jack *jack = device->device_data;
#ifdef CONFIG_SND_JACK_INPUT_DEV
struct snd_jack *jack = device->device_data;
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
/* If the input device is registered with the input subsystem
* then we need to use a different deallocator. */
/* If the input device is registered with the input subsystem
* then we need to use a different deallocator. */
else
input_free_device(jack->input_dev);
jack->input_dev = NULL;
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;
}
#endif /* CONFIG_SND_JACK_INPUT_DEV */
return 0;
}
snprintf(jack->name, sizeof(jack->name), "%s %s",
card->shortname, jack->id);
snprintf(jack->name, sizeof(jack->name), "%s %s",
card->shortname, jack->id);
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
jack->input_dev->name = jack->name;
jack->input_dev->name = jack->name;
if (err == 0)
jack->registered = 1;
if (err == 0)
jack->registered = 1;
+ mutex_unlock(&jack->input_dev_lock);
return err;
}
#endif /* CONFIG_SND_JACK_INPUT_DEV */
return err;
}
#endif /* CONFIG_SND_JACK_INPUT_DEV */
- /* don't creat input device for phantom jack */
- if (!phantom_jack) {
#ifdef CONFIG_SND_JACK_INPUT_DEV
#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();
int i;
jack->input_dev = input_allocate_device();
input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]);
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)
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
{
WARN_ON(jack->registered);
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
{
WARN_ON(jack->registered);
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
jack->input_dev->dev.parent = parent;
jack->input_dev->dev.parent = parent;
+ mutex_unlock(&jack->input_dev_lock);
}
EXPORT_SYMBOL(snd_jack_set_parent);
}
EXPORT_SYMBOL(snd_jack_set_parent);
/**
* snd_jack_report - Report the current status of a jack
/**
* 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
*
* @jack: The jack to report status for
* @status: The current status of the jack
status & jack_kctl->mask_bits);
#ifdef CONFIG_SND_JACK_INPUT_DEV
status & jack_kctl->mask_bits);
#ifdef CONFIG_SND_JACK_INPUT_DEV
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
}
input_sync(jack->input_dev);
}
input_sync(jack->input_dev);
+ mutex_unlock(&jack->input_dev_lock);
#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
EXPORT_SYMBOL(snd_jack_report);
#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
EXPORT_SYMBOL(snd_jack_report);