[alsa-devel] [PATCH 0/3] ASoC: DAPM/topology oops/use-after-free fixes
KASAN checks and more SOF stress-tests expose bugs that have apparently been there for a very long time.
Guennadi Liakhovetski (1): ASoC: topology: fix oops/use-after-free case with dai driver
Pierre-Louis Bossart (2): ASoC: dapm: fix out-of-bounds accesses to DAPM lookup tables ASoC: dapm: fix use-after-free issue with dailink sname
sound/soc/soc-core.c | 2 +- sound/soc/soc-dapm.c | 45 +++++++++++++++++++++++++++++++++++++++- sound/soc/soc-topology.c | 5 +++++ 3 files changed, 50 insertions(+), 2 deletions(-)
KASAN reports and additional traces point to out-of-bounds accesses to the dapm_up_seq and dapm_down_seq lookup tables. The indices used are larger than the array definition.
Fix by adding missing entries for the new widget types in these two lookup tables. The value of 10 was chosen since these widgets are not too critical for pop/click removals.
Also the sequences for the following widgets were not defined snd_soc_dapm_input snd_soc_dapm_output snd_soc_dapm_mic snd_soc_dapm_vmid snd_soc_dapm_siggen snd_soc_dapm_sink
Since their values defaulted to zero, use a more reasonable mid-level value of 10.
Fixes: 8a70b4544ef4 ('ASoC: dapm: Add new widget type for constructing DAPM graphs on DSPs.'). Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/soc-dapm.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2c4c13419539..f0d3d2d1a6bc 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -84,6 +84,19 @@ static int dapm_up_seq[] = { [snd_soc_dapm_mixer_named_ctl] = 8, [snd_soc_dapm_pga] = 9, [snd_soc_dapm_adc] = 10, + [snd_soc_dapm_input] = 10, + [snd_soc_dapm_output] = 10, + [snd_soc_dapm_mic] = 10, + [snd_soc_dapm_vmid] = 10, + [snd_soc_dapm_siggen] = 10, + [snd_soc_dapm_sink] = 10, + [snd_soc_dapm_buffer] = 10, + [snd_soc_dapm_scheduler] = 10, + [snd_soc_dapm_effect] = 10, + [snd_soc_dapm_src] = 10, + [snd_soc_dapm_asrc] = 10, + [snd_soc_dapm_encoder] = 10, + [snd_soc_dapm_decoder] = 10, [snd_soc_dapm_out_drv] = 11, [snd_soc_dapm_hp] = 11, [snd_soc_dapm_spk] = 11, @@ -113,6 +126,19 @@ static int dapm_down_seq[] = { [snd_soc_dapm_aif_out] = 10, [snd_soc_dapm_dai_in] = 10, [snd_soc_dapm_dai_out] = 10, + [snd_soc_dapm_input] = 10, + [snd_soc_dapm_output] = 10, + [snd_soc_dapm_mic] = 10, + [snd_soc_dapm_vmid] = 10, + [snd_soc_dapm_siggen] = 10, + [snd_soc_dapm_sink] = 10, + [snd_soc_dapm_buffer] = 10, + [snd_soc_dapm_scheduler] = 10, + [snd_soc_dapm_effect] = 10, + [snd_soc_dapm_src] = 10, + [snd_soc_dapm_asrc] = 10, + [snd_soc_dapm_encoder] = 10, + [snd_soc_dapm_decoder] = 10, [snd_soc_dapm_dai_link] = 11, [snd_soc_dapm_supply] = 12, [snd_soc_dapm_clock_supply] = 13,
On Fri, Feb 01, 2019 at 11:05:11AM -0600, Pierre-Louis Bossart wrote:
[snd_soc_dapm_mixer_named_ctl] = 8, [snd_soc_dapm_pga] = 9, [snd_soc_dapm_adc] = 10,
- [snd_soc_dapm_input] = 10,
- [snd_soc_dapm_output] = 10,
I'd put these at the same place as mics.
- [snd_soc_dapm_mic] = 10,
This is already in the table (at 5)?
- [snd_soc_dapm_vmid] = 10,
This should be goingn along with supplies and micbiases.
- [snd_soc_dapm_siggen] = 10,
Looks like this should go along with the other sources as mics.
- [snd_soc_dapm_sink] = 10,
This is an output so should be to the end, 11 looks like the right number.
- [snd_soc_dapm_buffer] = 10,
- [snd_soc_dapm_scheduler] = 10,
- [snd_soc_dapm_effect] = 10,
- [snd_soc_dapm_src] = 10,
- [snd_soc_dapm_asrc] = 10,
- [snd_soc_dapm_encoder] = 10,
- [snd_soc_dapm_decoder] = 10,
These feel like they should go along with PGAs.
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 --- sound/soc/soc-dapm.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f0d3d2d1a6bc..993b73fcd7b9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -321,7 +321,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 { @@ -2438,6 +2453,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); }
@@ -3495,6 +3511,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); }
The patch
ASoC: dapm: fix use-after-free issue with dailink sname
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 199ed3e81c49a621ce6fcb630ab9f30d92db6718 Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Date: Fri, 1 Feb 2019 11:05:12 -0600 Subject: [PATCH] ASoC: dapm: fix use-after-free issue with dailink sname
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 | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2c4c13419539..e71cd5b660ad 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -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); }
From: Guennadi Liakhovetski guennadi.liakhovetski@intel.com
rmmod/modprobe tests expose a kernel oops when accessing the dai driver pointer. This comes from the topology design which operates in multiple passes. Each object removal happens at a specific iteration, and the code checks for the iteration (order) number after the memory containing the order was freed.
Fix this be clearing a reference to the dai driver and check its validity to avoid dereferences.
Signed-off-by: Guennadi Liakhovetski guennadi.liakhovetski@intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/soc-core.c | 2 +- sound/soc/soc-topology.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d6b5edba8d2d..9dad2b1498c1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -965,7 +965,7 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order) { int err;
- if (!dai || !dai->probed || + if (!dai || !dai->probed || !dai->driver || dai->driver->remove_order != order) return;
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 23d421370e6c..bb7e5422a419 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -523,6 +523,7 @@ static void remove_dai(struct snd_soc_component *comp, { struct snd_soc_dai_driver *dai_drv = container_of(dobj, struct snd_soc_dai_driver, dobj); + struct snd_soc_dai *dai;
if (pass != SOC_TPLG_PASS_PCM_DAI) return; @@ -530,6 +531,10 @@ static void remove_dai(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->dai_unload) dobj->ops->dai_unload(comp, dobj);
+ list_for_each_entry(dai, &comp->dai_list, list) + if (dai->driver == dai_drv) + dai->driver = NULL; + kfree(dai_drv->name); list_del(&dobj->list); kfree(dai_drv);
The patch
ASoC: topology: fix oops/use-after-free case with dai driver
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 52abe6cc1866ac3d54612f5d80563e6608c0ddfc Mon Sep 17 00:00:00 2001
From: Guennadi Liakhovetski guennadi.liakhovetski@intel.com Date: Fri, 1 Feb 2019 11:05:13 -0600 Subject: [PATCH] ASoC: topology: fix oops/use-after-free case with dai driver
rmmod/modprobe tests expose a kernel oops when accessing the dai driver pointer. This comes from the topology design which operates in multiple passes. Each object removal happens at a specific iteration, and the code checks for the iteration (order) number after the memory containing the order was freed.
Fix this be clearing a reference to the dai driver and check its validity to avoid dereferences.
Signed-off-by: Guennadi Liakhovetski guennadi.liakhovetski@intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/soc-core.c | 2 +- sound/soc/soc-topology.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ea16c2b199ce..50617db05c46 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -956,7 +956,7 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order) { int err;
- if (!dai || !dai->probed || + if (!dai || !dai->probed || !dai->driver || dai->driver->remove_order != order) return;
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 045ef136903d..fc79ec6927e3 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -502,6 +502,7 @@ static void remove_dai(struct snd_soc_component *comp, { struct snd_soc_dai_driver *dai_drv = container_of(dobj, struct snd_soc_dai_driver, dobj); + struct snd_soc_dai *dai;
if (pass != SOC_TPLG_PASS_PCM_DAI) return; @@ -509,6 +510,10 @@ static void remove_dai(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->dai_unload) dobj->ops->dai_unload(comp, dobj);
+ list_for_each_entry(dai, &comp->dai_list, list) + if (dai->driver == dai_drv) + dai->driver = NULL; + kfree(dai_drv->name); list_del(&dobj->list); kfree(dai_drv);
participants (2)
-
Mark Brown
-
Pierre-Louis Bossart