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: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2013-01-10 18:21:56 +01:00
parent 08fb0d0ee1
commit 09b70e8509
4 changed files with 65 additions and 21 deletions

View file

@ -1086,9 +1086,16 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
struct hda_pincfg *pin; struct hda_pincfg *pin;
#ifdef CONFIG_SND_HDA_HWDEP #ifdef CONFIG_SND_HDA_HWDEP
pin = look_up_pincfg(codec, &codec->user_pins, nid); {
if (pin) unsigned int cfg = 0;
return pin->cfg; mutex_lock(&codec->user_mutex);
pin = look_up_pincfg(codec, &codec->user_pins, nid);
if (pin)
cfg = pin->cfg;
mutex_unlock(&codec->user_mutex);
if (cfg)
return cfg;
}
#endif #endif
pin = look_up_pincfg(codec, &codec->driver_pins, nid); pin = look_up_pincfg(codec, &codec->driver_pins, nid);
if (pin) if (pin)

View file

@ -845,6 +845,7 @@ struct hda_codec {
struct snd_array cvt_setups; /* audio convert setups */ struct snd_array cvt_setups; /* audio convert setups */
#ifdef CONFIG_SND_HDA_HWDEP #ifdef CONFIG_SND_HDA_HWDEP
struct mutex user_mutex;
struct snd_hwdep *hwdep; /* assigned hwdep device */ struct snd_hwdep *hwdep; /* assigned hwdep device */
struct snd_array init_verbs; /* additional init verbs */ struct snd_array init_verbs; /* additional init verbs */
struct snd_array hints; /* additional hints */ struct snd_array hints; /* additional hints */

View file

@ -148,6 +148,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif #endif
mutex_init(&codec->user_mutex);
snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); 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->hints, sizeof(struct hda_hint), 32);
snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16); 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 snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data; struct hda_codec *codec = hwdep->private_data;
int i, len = 0; int i, len = 0;
mutex_lock(&codec->user_mutex);
for (i = 0; i < codec->init_verbs.used; i++) { for (i = 0; i < codec->init_verbs.used; i++) {
struct hda_verb *v = snd_array_elem(&codec->init_verbs, i); struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
len += snprintf(buf + len, PAGE_SIZE - len, len += snprintf(buf + len, PAGE_SIZE - len,
"0x%02x 0x%03x 0x%04x\n", "0x%02x 0x%03x 0x%04x\n",
v->nid, v->verb, v->param); v->nid, v->verb, v->param);
} }
mutex_unlock(&codec->user_mutex);
return len; return len;
} }
@ -364,12 +367,16 @@ static int parse_init_verbs(struct hda_codec *codec, const char *buf)
return -EINVAL; return -EINVAL;
if (!nid || !verb) if (!nid || !verb)
return -EINVAL; return -EINVAL;
mutex_lock(&codec->user_mutex);
v = snd_array_new(&codec->init_verbs); v = snd_array_new(&codec->init_verbs);
if (!v) if (!v) {
mutex_unlock(&codec->user_mutex);
return -ENOMEM; return -ENOMEM;
}
v->nid = nid; v->nid = nid;
v->verb = verb; v->verb = verb;
v->param = param; v->param = param;
mutex_unlock(&codec->user_mutex);
return 0; return 0;
} }
@ -392,11 +399,13 @@ static ssize_t hints_show(struct device *dev,
struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data; struct hda_codec *codec = hwdep->private_data;
int i, len = 0; int i, len = 0;
mutex_lock(&codec->user_mutex);
for (i = 0; i < codec->hints.used; i++) { for (i = 0; i < codec->hints.used; i++) {
struct hda_hint *hint = snd_array_elem(&codec->hints, i); struct hda_hint *hint = snd_array_elem(&codec->hints, i);
len += snprintf(buf + len, PAGE_SIZE - len, len += snprintf(buf + len, PAGE_SIZE - len,
"%s = %s\n", hint->key, hint->val); "%s = %s\n", hint->key, hint->val);
} }
mutex_unlock(&codec->user_mutex);
return len; return len;
} }
@ -431,6 +440,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
{ {
char *key, *val; char *key, *val;
struct hda_hint *hint; struct hda_hint *hint;
int err = 0;
buf = skip_spaces(buf); buf = skip_spaces(buf);
if (!*buf || *buf == '#' || *buf == '\n') if (!*buf || *buf == '#' || *buf == '\n')
@ -450,26 +460,31 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
val = skip_spaces(val); val = skip_spaces(val);
remove_trail_spaces(key); remove_trail_spaces(key);
remove_trail_spaces(val); remove_trail_spaces(val);
mutex_lock(&codec->user_mutex);
hint = get_hint(codec, key); hint = get_hint(codec, key);
if (hint) { if (hint) {
/* replace */ /* replace */
kfree(hint->key); kfree(hint->key);
hint->key = key; hint->key = key;
hint->val = val; hint->val = val;
return 0; goto unlock;
} }
/* allocate a new hint entry */ /* allocate a new hint entry */
if (codec->hints.used >= MAX_HINTS) if (codec->hints.used >= MAX_HINTS)
hint = NULL; hint = NULL;
else else
hint = snd_array_new(&codec->hints); hint = snd_array_new(&codec->hints);
if (!hint) { if (hint) {
kfree(key); hint->key = key;
return -ENOMEM; hint->val = val;
} else {
err = -ENOMEM;
} }
hint->key = key; unlock:
hint->val = val; mutex_unlock(&codec->user_mutex);
return 0; if (err)
kfree(key);
return err;
} }
static ssize_t hints_store(struct device *dev, static ssize_t hints_store(struct device *dev,
@ -489,11 +504,13 @@ static ssize_t pin_configs_show(struct hda_codec *codec,
char *buf) char *buf)
{ {
int i, len = 0; int i, len = 0;
mutex_lock(&codec->user_mutex);
for (i = 0; i < list->used; i++) { for (i = 0; i < list->used; i++) {
struct hda_pincfg *pin = snd_array_elem(list, i); struct hda_pincfg *pin = snd_array_elem(list, i);
len += sprintf(buf + len, "0x%02x 0x%08x\n", len += sprintf(buf + len, "0x%02x 0x%08x\n",
pin->nid, pin->cfg); pin->nid, pin->cfg);
} }
mutex_unlock(&codec->user_mutex);
return len; 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) 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) if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
return -EINVAL; return -EINVAL;
if (!nid) if (!nid)
return -EINVAL; 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, 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) 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) if (!p || !*p)
return -ENOENT; ret = -ENOENT;
switch (toupper(*p)) { else {
case 'T': /* true */ switch (toupper(*p)) {
case 'Y': /* yes */ case 'T': /* true */
case '1': case 'Y': /* yes */
return 1; case '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); EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);

View file

@ -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, static inline int get_int_hint(struct hda_codec *codec, const char *key,
int *valp) int *valp)
{ {
#ifdef CONFIG_SND_HDA_RECONFIG
const char *p; const char *p;
mutex_lock(&codec->user_mutex);
p = snd_hda_get_hint(codec, key); p = snd_hda_get_hint(codec, key);
if (p) { if (p) {
unsigned long val; unsigned long val;
if (!strict_strtoul(p, 0, &val)) { if (!strict_strtoul(p, 0, &val)) {
*valp = val; *valp = val;
mutex_unlock(&codec->user_mutex);
return 1; return 1;
} }
} }
mutex_unlock(&codec->user_mutex);
#endif
return 0; return 0;
} }