ALSA: hda - Protect user-defined arrays via mutex
authorTakashi Iwai <tiwai@suse.de>
Thu, 10 Jan 2013 17:21:56 +0000 (18:21 +0100)
committerTakashi Iwai <tiwai@suse.de>
Sat, 12 Jan 2013 07:44:45 +0000 (08:44 +0100)
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>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_hwdep.c
sound/pci/hda/patch_sigmatel.c

index 0a531f2f9255c012edb06b090ec552c9ee29ebfc..b28e4031b8a1adeb988598ceb2804deb5534c205 100644 (file)
@@ -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)
index 4c4f1660e6548f344d2f6d36d88fac6b6aecfd5d..61085b311059bf03ac12c571b264d5509f59694d 100644 (file)
@@ -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 */
index a5c9411bb3670f7a095e7bc70b8ad0b90a27d4ae..2dddf7fbebcc4811f28ce0ad717af953cf4379b6 100644 (file)
@@ -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);
 
index a86547ca17c861f51ce815eeb5e918e8f57fcc42..d3a81f10fe5c85ec80327b3d1dbba782fc1adac2 100644 (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,
                               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;
 }