Skip to content

Commit cae336f

Browse files
KaiChieh ChuangSoumya Managoli
KaiChieh Chuang
authored and
Soumya Managoli
committed
From bbfaa7d36c1eb465f120f2a3dfe25c1fe022195d Mon Sep 17 00:00:00 2001
From: KaiChieh Chuang <[email protected]> Date: Thu, 7 Mar 2019 07:51:09 +0800 Subject: [PATCH] ASoC: dpcm: prevent snd_soc_dpcm use after free The dpcm get from fe_clients/be_clients may be free before use Add a spin lock at snd_soc_card level, to protect the dpcm instance. The lock may be used in atomic context, so use spin lock. possible race condition between void dpcm_be_disconnect( ... list_del(&dpcm->list_be); list_del(&dpcm->list_fe); kfree(dpcm); ... and for_each_dpcm_fe() for_each_dpcm_be*() race condition example Thread 1: snd_soc_dapm_mixer_update_power() -> soc_dpcm_runtime_update() -> dpcm_be_disconnect() -> kfree(dpcm); Thread 2: dpcm_fe_dai_trigger() -> dpcm_be_dai_trigger() -> snd_soc_dpcm_can_be_free_stop() -> if (dpcm->fe == fe) Excpetion Scenario: two FE link to same BE FE1 -> BE FE2 -> Thread 1: switch of mixer between FE2 -> BE Thread 2: pcm_stop FE1 Exception: Unable to handle kernel paging request at virtual address dead0000000000e0 pc=<> [<ffffff8960e2cd10>] dpcm_be_dai_trigger+0x29c/0x47c sound/soc/soc-pcm.c:3226 if (dpcm->fe == fe) lr=<> [<ffffff8960e2f694>] dpcm_fe_dai_do_trigger+0x94/0x26c Backtrace: [<ffffff89602dba80>] notify_die+0x68/0xb8 [<ffffff896028c7dc>] die+0x118/0x2a8 [<ffffff89602a2f84>] __do_kernel_fault+0x13c/0x14c [<ffffff89602a27f4>] do_translation_fault+0x64/0xa0 [<ffffff8960280cf8>] do_mem_abort+0x4c/0xd0 [<ffffff8960282ad0>] el1_da+0x24/0x40 [<ffffff8960e2cd10>] dpcm_be_dai_trigger+0x29c/0x47c [<ffffff8960e2f694>] dpcm_fe_dai_do_trigger+0x94/0x26c [<ffffff8960e2edec>] dpcm_fe_dai_trigger+0x3c/0x44 [<ffffff8960de5588>] snd_pcm_do_stop+0x50/0x5c [<ffffff8960dded24>] snd_pcm_action+0xb4/0x13c [<ffffff8960ddfdb4>] snd_pcm_drop+0xa0/0x128 [<ffffff8960de69bc>] snd_pcm_common_ioctl+0x9d8/0x30f0 [<ffffff8960de1cac>] snd_pcm_ioctl_compat+0x29c/0x2f14 [<ffffff89604c9d60>] compat_SyS_ioctl+0x128/0x244 [<ffffff8960283740>] el0_svc_naked+0x34/0x38 [<ffffffffffffffff>] 0xffffffffffffffff. Change-Id: Ia3df59e2881f7242d3d618c33d4fdf7e51b31859 Signed-off-by: KaiChieh Chuang <[email protected]> Signed-off-by: Mark Brown <[email protected]> Git-commit: bbfaa7d36c1eb465f120f2a3dfe25c1fe022195d Git-repo: https://android.googlesource.com/kernel/common/ Signed-off-by: Soumya Managoli <[email protected]>
1 parent 3dc0765 commit cae336f

File tree

3 files changed

+29
-7
lines changed

3 files changed

+29
-7
lines changed

include/sound/soc.h

+2
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,8 @@ struct snd_soc_card {
10701070
struct mutex dapm_mutex;
10711071
struct mutex dapm_power_mutex;
10721072

1073+
spinlock_t dpcm_lock;
1074+
10731075
bool instantiated;
10741076
bool topology_shortname_created;
10751077

sound/soc/soc-core.c

+1
Original file line numberDiff line numberDiff line change
@@ -2792,6 +2792,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
27922792
mutex_init(&card->mutex);
27932793
mutex_init(&card->dapm_mutex);
27942794
mutex_init(&card->dapm_power_mutex);
2795+
spin_lock_init(&card->dpcm_lock);
27952796

27962797
ret = snd_soc_instantiate_card(card);
27972798
if (ret != 0)

sound/soc/soc-pcm.c

+26-7
Original file line numberDiff line numberDiff line change
@@ -1340,8 +1340,10 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
13401340
dpcm->fe = fe;
13411341
be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
13421342
dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
1343+
spin_lock(&fe->card->dpcm_lock);
13431344
list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
13441345
list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
1346+
spin_unlock(&fe->card->dpcm_lock);
13451347

13461348
dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
13471349
stream ? "capture" : "playback", fe->dai_link->name,
@@ -1406,8 +1408,10 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
14061408
#ifdef CONFIG_DEBUG_FS
14071409
debugfs_remove(dpcm->debugfs_state);
14081410
#endif
1411+
spin_lock(&fe->card->dpcm_lock);
14091412
list_del(&dpcm->list_be);
14101413
list_del(&dpcm->list_fe);
1414+
spin_unlock(&fe->card->dpcm_lock);
14111415
kfree(dpcm);
14121416
}
14131417
}
@@ -1672,9 +1676,11 @@ void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
16721676
{
16731677
struct snd_soc_dpcm *dpcm;
16741678

1679+
spin_lock(&fe->card->dpcm_lock);
16751680
list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
16761681
dpcm->be->dpcm[stream].runtime_update =
16771682
SND_SOC_DPCM_UPDATE_NO;
1683+
spin_unlock(&fe->card->dpcm_lock);
16781684
}
16791685

16801686
static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
@@ -3044,11 +3050,13 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
30443050
dpcm_be_dai_shutdown(fe, stream);
30453051
disconnect:
30463052
/* disconnect any non started BEs */
3053+
spin_lock(&fe->card->dpcm_lock);
30473054
list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
30483055
struct snd_soc_pcm_runtime *be = dpcm->be;
30493056
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
30503057
dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
30513058
}
3059+
spin_unlock(&fe->card->dpcm_lock);
30523060

30533061
return ret;
30543062
}
@@ -3662,7 +3670,9 @@ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
36623670
{
36633671
struct snd_soc_dpcm *dpcm;
36643672
int state;
3673+
int ret = 1;
36653674

3675+
spin_lock(&fe->card->dpcm_lock);
36663676
list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
36673677

36683678
if (dpcm->fe == fe)
@@ -3671,12 +3681,15 @@ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
36713681
state = dpcm->fe->dpcm[stream].state;
36723682
if (state == SND_SOC_DPCM_STATE_START ||
36733683
state == SND_SOC_DPCM_STATE_PAUSED ||
3674-
state == SND_SOC_DPCM_STATE_SUSPEND)
3675-
return 0;
3684+
state == SND_SOC_DPCM_STATE_SUSPEND) {
3685+
ret = 0;
3686+
break;
3687+
}
36763688
}
3689+
spin_unlock(&fe->card->dpcm_lock);
36773690

36783691
/* it's safe to free/stop this BE DAI */
3679-
return 1;
3692+
return ret;
36803693
}
36813694
EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
36823695

@@ -3689,7 +3702,9 @@ int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
36893702
{
36903703
struct snd_soc_dpcm *dpcm;
36913704
int state;
3705+
int ret = 1;
36923706

3707+
spin_lock(&fe->card->dpcm_lock);
36933708
list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
36943709

36953710
if (dpcm->fe == fe)
@@ -3699,12 +3714,15 @@ int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
36993714
if (state == SND_SOC_DPCM_STATE_START ||
37003715
state == SND_SOC_DPCM_STATE_PAUSED ||
37013716
state == SND_SOC_DPCM_STATE_SUSPEND ||
3702-
state == SND_SOC_DPCM_STATE_PREPARE)
3703-
return 0;
3717+
state == SND_SOC_DPCM_STATE_PREPARE) {
3718+
ret = 0;
3719+
break;
3720+
}
37043721
}
3722+
spin_unlock(&fe->card->dpcm_lock);
37053723

37063724
/* it's safe to change hw_params */
3707-
return 1;
3725+
return ret;
37083726
}
37093727
EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
37103728

@@ -3770,6 +3788,7 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
37703788
goto out;
37713789
}
37723790

3791+
spin_lock(&fe->card->dpcm_lock);
37733792
list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
37743793
struct snd_soc_pcm_runtime *be = dpcm->be;
37753794
params = &dpcm->hw_params;
@@ -3790,7 +3809,7 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
37903809
params_channels(params),
37913810
params_rate(params));
37923811
}
3793-
3812+
spin_unlock(&fe->card->dpcm_lock);
37943813
out:
37953814
return offset;
37963815
}

0 commit comments

Comments
 (0)