forked from Minki/linux
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:
parent
08fb0d0ee1
commit
09b70e8509
@ -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
|
||||
pin = look_up_pincfg(codec, &codec->user_pins, nid);
|
||||
if (pin)
|
||||
return pin->cfg;
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
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
|
||||
pin = look_up_pincfg(codec, &codec->driver_pins, nid);
|
||||
if (pin)
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
} else {
|
||||
err = -ENOMEM;
|
||||
}
|
||||
hint->key = key;
|
||||
hint->val = val;
|
||||
return 0;
|
||||
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;
|
||||
switch (toupper(*p)) {
|
||||
case 'T': /* true */
|
||||
case 'Y': /* yes */
|
||||
case '1':
|
||||
return 1;
|
||||
ret = -ENOENT;
|
||||
else {
|
||||
switch (toupper(*p)) {
|
||||
case 'T': /* true */
|
||||
case 'Y': /* yes */
|
||||
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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user