Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: mediatek: mt8195: fix use-after-free in driver remove path

During mt8195_afe_init_clock(), mt8195_audsys_clk_register() was called
followed by several other devm functions. At mt8195_afe_deinit_clock()
located at mt8195_afe_pcm_dev_remove(), mt8195_audsys_clk_unregister()
was called.

However, there was an issue with the order in which these functions were
called. Specifically, the remove callback of platform_driver was called
before devres released the resource, resulting in a use-after-free issue
during remove time.

At probe time, the order of calls was:
1. mt8195_audsys_clk_register
2. afe_priv->clk = devm_kcalloc
3. afe_priv->clk[i] = devm_clk_get

At remove time, the order of calls was:
1. mt8195_audsys_clk_unregister
3. free afe_priv->clk[i]
2. free afe_priv->clk

To resolve the problem, we can utilize devm_add_action_or_reset() in
mt8195_audsys_clk_register() so that the remove order can be changed to
3->2->1.

Fixes: 6746cc858259 ("ASoC: mediatek: mt8195: add platform driver")
Signed-off-by: Trevor Wu <trevor.wu@mediatek.com>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://lore.kernel.org/r/20230601033318.10408-3-trevor.wu@mediatek.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Trevor Wu and committed by
Mark Brown
dc93f0dc fd67a7a1

+24 -34
-5
sound/soc/mediatek/mt8195/mt8195-afe-clk.c
··· 410 410 return 0; 411 411 } 412 412 413 - void mt8195_afe_deinit_clock(struct mtk_base_afe *afe) 414 - { 415 - mt8195_audsys_clk_unregister(afe); 416 - } 417 - 418 413 int mt8195_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk) 419 414 { 420 415 int ret;
-1
sound/soc/mediatek/mt8195/mt8195-afe-clk.h
··· 101 101 int mt8195_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll); 102 102 int mt8195_afe_get_default_mclk_source_by_rate(int rate); 103 103 int mt8195_afe_init_clock(struct mtk_base_afe *afe); 104 - void mt8195_afe_deinit_clock(struct mtk_base_afe *afe); 105 104 int mt8195_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk); 106 105 void mt8195_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk); 107 106 int mt8195_afe_prepare_clk(struct mtk_base_afe *afe, struct clk *clk);
-4
sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
··· 3255 3255 3256 3256 static void mt8195_afe_pcm_dev_remove(struct platform_device *pdev) 3257 3257 { 3258 - struct mtk_base_afe *afe = platform_get_drvdata(pdev); 3259 - 3260 3258 snd_soc_unregister_component(&pdev->dev); 3261 3259 3262 3260 pm_runtime_disable(&pdev->dev); 3263 3261 if (!pm_runtime_status_suspended(&pdev->dev)) 3264 3262 mt8195_afe_runtime_suspend(&pdev->dev); 3265 - 3266 - mt8195_afe_deinit_clock(afe); 3267 3263 } 3268 3264 3269 3265 static const struct of_device_id mt8195_afe_pcm_dt_match[] = {
+24 -23
sound/soc/mediatek/mt8195/mt8195-audsys-clk.c
··· 148 148 GATE_AUD6(CLK_AUD_GASRC19, "aud_gasrc19", "top_asm_h", 19), 149 149 }; 150 150 151 + static void mt8195_audsys_clk_unregister(void *data) 152 + { 153 + struct mtk_base_afe *afe = data; 154 + struct mt8195_afe_private *afe_priv = afe->platform_priv; 155 + struct clk *clk; 156 + struct clk_lookup *cl; 157 + int i; 158 + 159 + if (!afe_priv) 160 + return; 161 + 162 + for (i = 0; i < CLK_AUD_NR_CLK; i++) { 163 + cl = afe_priv->lookup[i]; 164 + if (!cl) 165 + continue; 166 + 167 + clk = cl->clk; 168 + clk_unregister_gate(clk); 169 + 170 + clkdev_drop(cl); 171 + } 172 + } 173 + 151 174 int mt8195_audsys_clk_register(struct mtk_base_afe *afe) 152 175 { 153 176 struct mt8195_afe_private *afe_priv = afe->platform_priv; ··· 211 188 afe_priv->lookup[i] = cl; 212 189 } 213 190 214 - return 0; 215 - } 216 - 217 - void mt8195_audsys_clk_unregister(struct mtk_base_afe *afe) 218 - { 219 - struct mt8195_afe_private *afe_priv = afe->platform_priv; 220 - struct clk *clk; 221 - struct clk_lookup *cl; 222 - int i; 223 - 224 - if (!afe_priv) 225 - return; 226 - 227 - for (i = 0; i < CLK_AUD_NR_CLK; i++) { 228 - cl = afe_priv->lookup[i]; 229 - if (!cl) 230 - continue; 231 - 232 - clk = cl->clk; 233 - clk_unregister_gate(clk); 234 - 235 - clkdev_drop(cl); 236 - } 191 + return devm_add_action_or_reset(afe->dev, mt8195_audsys_clk_unregister, afe); 237 192 }
-1
sound/soc/mediatek/mt8195/mt8195-audsys-clk.h
··· 10 10 #define _MT8195_AUDSYS_CLK_H_ 11 11 12 12 int mt8195_audsys_clk_register(struct mtk_base_afe *afe); 13 - void mt8195_audsys_clk_unregister(struct mtk_base_afe *afe); 14 13 15 14 #endif