mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	ALSA: jack: Access input_dev under mutex
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>
This commit is contained in:
		
							parent
							
								
									8f06bd1f89
								
							
						
					
					
						commit
						1b6a6fc528
					
				
					 2 changed files with 28 additions and 7 deletions
				
			
		| 
						 | 
					@ -62,6 +62,7 @@ struct snd_jack {
 | 
				
			||||||
	const char *id;
 | 
						const char *id;
 | 
				
			||||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
 | 
					#ifdef CONFIG_SND_JACK_INPUT_DEV
 | 
				
			||||||
	struct input_dev *input_dev;
 | 
						struct input_dev *input_dev;
 | 
				
			||||||
 | 
						struct mutex input_dev_lock;
 | 
				
			||||||
	int registered;
 | 
						int registered;
 | 
				
			||||||
	int type;
 | 
						int type;
 | 
				
			||||||
	char name[100];
 | 
						char name[100];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,8 +42,11 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
 | 
				
			||||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
 | 
					#ifdef CONFIG_SND_JACK_INPUT_DEV
 | 
				
			||||||
	struct snd_jack *jack = device->device_data;
 | 
						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;
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* If the input device is registered with the input subsystem
 | 
						/* If the input device is registered with the input subsystem
 | 
				
			||||||
	 * then we need to use a different deallocator. */
 | 
						 * then we need to use a different deallocator. */
 | 
				
			||||||
| 
						 | 
					@ -52,6 +55,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		input_free_device(jack->input_dev);
 | 
							input_free_device(jack->input_dev);
 | 
				
			||||||
	jack->input_dev = NULL;
 | 
						jack->input_dev = NULL;
 | 
				
			||||||
 | 
						mutex_unlock(&jack->input_dev_lock);
 | 
				
			||||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
 | 
					#endif /* CONFIG_SND_JACK_INPUT_DEV */
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -90,8 +94,11 @@ static int snd_jack_dev_register(struct snd_device *device)
 | 
				
			||||||
	snprintf(jack->name, sizeof(jack->name), "%s %s",
 | 
						snprintf(jack->name, sizeof(jack->name), "%s %s",
 | 
				
			||||||
		 card->shortname, jack->id);
 | 
							 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;
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	jack->input_dev->name = jack->name;
 | 
						jack->input_dev->name = jack->name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,6 +123,7 @@ static int snd_jack_dev_register(struct snd_device *device)
 | 
				
			||||||
	if (err == 0)
 | 
						if (err == 0)
 | 
				
			||||||
		jack->registered = 1;
 | 
							jack->registered = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_unlock(&jack->input_dev_lock);
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
 | 
					#endif /* CONFIG_SND_JACK_INPUT_DEV */
 | 
				
			||||||
| 
						 | 
					@ -517,9 +525,11 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* 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;
 | 
							int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		jack->input_dev = input_allocate_device();
 | 
							jack->input_dev = input_allocate_device();
 | 
				
			||||||
| 
						 | 
					@ -537,8 +547,8 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
 | 
				
			||||||
				input_set_capability(jack->input_dev, EV_SW,
 | 
									input_set_capability(jack->input_dev, EV_SW,
 | 
				
			||||||
						     jack_switch_types[i]);
 | 
											     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);
 | 
						err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
| 
						 | 
					@ -578,10 +588,14 @@ EXPORT_SYMBOL(snd_jack_new);
 | 
				
			||||||
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
 | 
					void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	WARN_ON(jack->registered);
 | 
						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;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -629,6 +643,8 @@ EXPORT_SYMBOL(snd_jack_set_key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 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
 | 
					 * @jack:   The jack to report status for
 | 
				
			||||||
 * @status: The current status of the jack
 | 
					 * @status: The current status of the jack
 | 
				
			||||||
| 
						 | 
					@ -654,8 +670,11 @@ void snd_jack_report(struct snd_jack *jack, int status)
 | 
				
			||||||
					     status & jack_kctl->mask_bits);
 | 
										     status & jack_kctl->mask_bits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
 | 
					#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;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
 | 
						for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
 | 
				
			||||||
		int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
 | 
							int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
 | 
				
			||||||
| 
						 | 
					@ -675,6 +694,7 @@ void snd_jack_report(struct snd_jack *jack, int status)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	input_sync(jack->input_dev);
 | 
						input_sync(jack->input_dev);
 | 
				
			||||||
 | 
						mutex_unlock(&jack->input_dev_lock);
 | 
				
			||||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
 | 
					#endif /* CONFIG_SND_JACK_INPUT_DEV */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(snd_jack_report);
 | 
					EXPORT_SYMBOL(snd_jack_report);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue