ASoC: dapm: fix use-after-free issue with dailink sname
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Fri, 1 Feb 2019 17:05:12 +0000 (11:05 -0600)
committerMark Brown <broonie@kernel.org>
Sat, 2 Feb 2019 16:13:09 +0000 (17:13 +0100)
Commit 7620fe9161ce ("ASoC: topology: fix memory leak in
soc_tplg_dapm_widget_create") fixed a memory leak issue, but
additional tests and KASAN reports show a use-after-free in soc-dapm.

The widgets are created with a kmemdup operating on a template. The
"name" string is also duplicated, but the "sname" string is not. As a
result, when the template is freed after widget creation, its sname
string is still used.

Fix by explicitly duplicating the "sname" string, and freeing it when
required.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/soc-dapm.c

index 2c4c134195392936bc03f9440c013360ac853a31..e71cd5b660ad09688176134c706e31a3b061a587 100644 (file)
@@ -295,7 +295,22 @@ EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
        const struct snd_soc_dapm_widget *_widget)
 {
-       return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
+       struct snd_soc_dapm_widget *w;
+
+       w = kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
+       if (!w)
+               return NULL;
+
+       /*
+        * w->name is duplicated in caller, but w->sname isn't.
+        * Duplicate it here if defined
+        */
+       if (_widget->sname) {
+               w->sname = kstrdup_const(_widget->sname, GFP_KERNEL);
+               if (!w->sname)
+                       return NULL;
+       }
+       return w;
 }
 
 struct dapm_kcontrol_data {
@@ -2412,6 +2427,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
 
        kfree(w->kcontrols);
        kfree_const(w->name);
+       kfree_const(w->sname);
        kfree(w);
 }
 
@@ -3469,6 +3485,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
        else
                w->name = kstrdup_const(widget->name, GFP_KERNEL);
        if (w->name == NULL) {
+               kfree_const(w->sname);
                kfree(w);
                return ERR_PTR(-ENOMEM);
        }