Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09b70e85 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda - Protect user-defined arrays via mutex



The pincfgs, init_verbs and hints set by sysfs or patch might be
changed dynamically on the fly, thus we need to protect it.
Add a simple protection via a mutex.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 08fb0d0e
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -1086,9 +1086,16 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
	struct hda_pincfg *pin;

#ifdef CONFIG_SND_HDA_HWDEP
	{
		unsigned int cfg = 0;
		mutex_lock(&codec->user_mutex);
		pin = look_up_pincfg(codec, &codec->user_pins, nid);
		if (pin)
		return pin->cfg;
			cfg = pin->cfg;
		mutex_unlock(&codec->user_mutex);
		if (cfg)
			return cfg;
	}
#endif
	pin = look_up_pincfg(codec, &codec->driver_pins, nid);
	if (pin)
+1 −0
Original line number Diff line number Diff line
@@ -845,6 +845,7 @@ struct hda_codec {
	struct snd_array cvt_setups;	/* audio convert setups */

#ifdef CONFIG_SND_HDA_HWDEP
	struct mutex user_mutex;
	struct snd_hwdep *hwdep;	/* assigned hwdep device */
	struct snd_array init_verbs;	/* additional init verbs */
	struct snd_array hints;		/* additional hints */
+49 −18
Original line number Diff line number Diff line
@@ -148,6 +148,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
	hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif

	mutex_init(&codec->user_mutex);
	snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
	snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
	snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
@@ -346,12 +347,14 @@ static ssize_t init_verbs_show(struct device *dev,
	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
	struct hda_codec *codec = hwdep->private_data;
	int i, len = 0;
	mutex_lock(&codec->user_mutex);
	for (i = 0; i < codec->init_verbs.used; i++) {
		struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
		len += snprintf(buf + len, PAGE_SIZE - len,
				"0x%02x 0x%03x 0x%04x\n",
				v->nid, v->verb, v->param);
	}
	mutex_unlock(&codec->user_mutex);
	return len;
}

@@ -364,12 +367,16 @@ static int parse_init_verbs(struct hda_codec *codec, const char *buf)
		return -EINVAL;
	if (!nid || !verb)
		return -EINVAL;
	mutex_lock(&codec->user_mutex);
	v = snd_array_new(&codec->init_verbs);
	if (!v)
	if (!v) {
		mutex_unlock(&codec->user_mutex);
		return -ENOMEM;
	}
	v->nid = nid;
	v->verb = verb;
	v->param = param;
	mutex_unlock(&codec->user_mutex);
	return 0;
}

@@ -392,11 +399,13 @@ static ssize_t hints_show(struct device *dev,
	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
	struct hda_codec *codec = hwdep->private_data;
	int i, len = 0;
	mutex_lock(&codec->user_mutex);
	for (i = 0; i < codec->hints.used; i++) {
		struct hda_hint *hint = snd_array_elem(&codec->hints, i);
		len += snprintf(buf + len, PAGE_SIZE - len,
				"%s = %s\n", hint->key, hint->val);
	}
	mutex_unlock(&codec->user_mutex);
	return len;
}

@@ -431,6 +440,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
{
	char *key, *val;
	struct hda_hint *hint;
	int err = 0;

	buf = skip_spaces(buf);
	if (!*buf || *buf == '#' || *buf == '\n')
@@ -450,26 +460,31 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
	val = skip_spaces(val);
	remove_trail_spaces(key);
	remove_trail_spaces(val);
	mutex_lock(&codec->user_mutex);
	hint = get_hint(codec, key);
	if (hint) {
		/* replace */
		kfree(hint->key);
		hint->key = key;
		hint->val = val;
		return 0;
		goto unlock;
	}
	/* allocate a new hint entry */
	if (codec->hints.used >= MAX_HINTS)
		hint = NULL;
	else
		hint = snd_array_new(&codec->hints);
	if (!hint) {
		kfree(key);
		return -ENOMEM;
	}
	if (hint) {
		hint->key = key;
		hint->val = val;
	return 0;
	} else {
		err = -ENOMEM;
	}
 unlock:
	mutex_unlock(&codec->user_mutex);
	if (err)
		kfree(key);
	return err;
}

static ssize_t hints_store(struct device *dev,
@@ -489,11 +504,13 @@ static ssize_t pin_configs_show(struct hda_codec *codec,
				char *buf)
{
	int i, len = 0;
	mutex_lock(&codec->user_mutex);
	for (i = 0; i < list->used; i++) {
		struct hda_pincfg *pin = snd_array_elem(list, i);
		len += sprintf(buf + len, "0x%02x 0x%08x\n",
			       pin->nid, pin->cfg);
	}
	mutex_unlock(&codec->user_mutex);
	return len;
}

@@ -528,13 +545,16 @@ static ssize_t driver_pin_configs_show(struct device *dev,

static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
{
	int nid, cfg;
	int nid, cfg, err;

	if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
		return -EINVAL;
	if (!nid)
		return -EINVAL;
	return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
	mutex_lock(&codec->user_mutex);
	err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
	mutex_unlock(&codec->user_mutex);
	return err;
}

static ssize_t user_pin_configs_store(struct device *dev,
@@ -600,16 +620,27 @@ EXPORT_SYMBOL_HDA(snd_hda_get_hint);

int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
	const char *p = snd_hda_get_hint(codec, key);
	const char *p;
	int ret;

	mutex_lock(&codec->user_mutex);
	p = snd_hda_get_hint(codec, key);
	if (!p || !*p)
		return -ENOENT;
		ret = -ENOENT;
	else {
		switch (toupper(*p)) {
		case 'T': /* true */
		case 'Y': /* yes */
		case '1':
		return 1;
			ret = 1;
			break;
		default:
			ret = 0;
			break;
		}
	return 0;
	}
	mutex_unlock(&codec->user_mutex);
	return ret;
}
EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);

+5 −0
Original line number Diff line number Diff line
@@ -4298,15 +4298,20 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
static inline int get_int_hint(struct hda_codec *codec, const char *key,
			       int *valp)
{
#ifdef CONFIG_SND_HDA_RECONFIG
	const char *p;
	mutex_lock(&codec->user_mutex);
	p = snd_hda_get_hint(codec, key);
	if (p) {
		unsigned long val;
		if (!strict_strtoul(p, 0, &val)) {
			*valp = val;
			mutex_unlock(&codec->user_mutex);
			return 1;
		}
	}
	mutex_unlock(&codec->user_mutex);
#endif
	return 0;
}