Skip to content

Commit bc51a6f

Browse files
Wu Zhiganglgirdwood
authored andcommitted
ASoC:topology:bug fix:check return value avoid oops.
check the return value to free the kcontrols instance to avoid oops caused by the pointer dereference. Signed-off-by: Wu Zhigang <zhigang.wu@linux.intel.com> [guennadi.liakhovetski@intel.com add handling of .autodisable=1 cases]
1 parent 801d176 commit bc51a6f

File tree

1 file changed

+90
-9
lines changed

1 file changed

+90
-9
lines changed

sound/soc/soc-dapm.c

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,12 +2410,11 @@ static void dapm_free_path(struct snd_soc_dapm_path *path)
24102410
kfree(path);
24112411
}
24122412

2413-
void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
2413+
static void snd_soc_dapm_free_widget_data(struct snd_soc_dapm_widget *w)
24142414
{
24152415
struct snd_soc_dapm_path *p, *next_p;
24162416
enum snd_soc_dapm_direction dir;
24172417

2418-
list_del(&w->list);
24192418
/*
24202419
* remove source and sink paths associated to this widget.
24212420
* While removing the path, remove reference to it from both
@@ -2428,6 +2427,12 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
24282427

24292428
kfree(w->kcontrols);
24302429
kfree_const(w->name);
2430+
}
2431+
2432+
void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
2433+
{
2434+
list_del(&w->list);
2435+
snd_soc_dapm_free_widget_data(w);
24312436
kfree(w);
24322437
}
24332438

@@ -3046,11 +3051,16 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
30463051
*/
30473052
int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
30483053
{
3049-
struct snd_soc_dapm_widget *w;
3054+
struct snd_soc_dapm_widget *w, *last;
30503055
unsigned int val;
3056+
int ret = 0;
30513057

30523058
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
30533059

3060+
/*
3061+
* widgets with the snd_soc_dapm_kcontrol ID and .num_controls = 0 can
3062+
* be appended to the list while scanning it, this is safe.
3063+
*/
30543064
list_for_each_entry(w, &card->widgets, list)
30553065
{
30563066
if (w->new)
@@ -3061,33 +3071,39 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
30613071
sizeof(struct snd_kcontrol *),
30623072
GFP_KERNEL);
30633073
if (!w->kcontrols) {
3064-
mutex_unlock(&card->dapm_mutex);
3065-
return -ENOMEM;
3074+
ret = -ENOMEM;
3075+
goto out_free;
30663076
}
30673077
}
30683078

30693079
switch(w->id) {
30703080
case snd_soc_dapm_switch:
30713081
case snd_soc_dapm_mixer:
30723082
case snd_soc_dapm_mixer_named_ctl:
3073-
dapm_new_mixer(w);
3083+
ret = dapm_new_mixer(w);
30743084
break;
30753085
case snd_soc_dapm_mux:
30763086
case snd_soc_dapm_demux:
3077-
dapm_new_mux(w);
3087+
ret = dapm_new_mux(w);
30783088
break;
30793089
case snd_soc_dapm_pga:
30803090
case snd_soc_dapm_effect:
30813091
case snd_soc_dapm_out_drv:
3082-
dapm_new_pga(w);
3092+
ret = dapm_new_pga(w);
30833093
break;
30843094
case snd_soc_dapm_dai_link:
3085-
dapm_new_dai_link(w);
3095+
ret = dapm_new_dai_link(w);
30863096
break;
30873097
default:
30883098
break;
30893099
}
30903100

3101+
if (ret < 0) {
3102+
kfree(w->kcontrols);
3103+
w->kcontrols = NULL;
3104+
goto out_free;
3105+
}
3106+
30913107
/* Read the initial power state from the device */
30923108
if (w->reg >= 0) {
30933109
soc_dapm_read(w->dapm, w->reg, &val);
@@ -3096,6 +3112,11 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
30963112
if (val == w->on_val)
30973113
w->power = 1;
30983114
}
3115+
}
3116+
3117+
list_for_each_entry(w, &card->widgets, list) {
3118+
if (w->new)
3119+
continue;
30993120

31003121
w->new = 1;
31013122

@@ -3106,6 +3127,66 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
31063127
dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
31073128
mutex_unlock(&card->dapm_mutex);
31083129
return 0;
3130+
3131+
out_free:
3132+
last = w;
3133+
3134+
/*
3135+
* If any new widgets have been created above for .autodisable = 1
3136+
* controls, they are also on this list, but at its very end. We're
3137+
* processing an error case, so the loop above was interrupted before
3138+
* reaching dynamically added widgets, since the latter cannot fail -
3139+
* their .num_controls = 0, so allocation cannot fail, and their type is
3140+
* snd_soc_dapm_kcontrol, those cannot fail either. Therefore "last"
3141+
* points to a widget before the first dynamic one.
3142+
*/
3143+
list_for_each_entry(w, &card->widgets, list) {
3144+
unsigned int i;
3145+
3146+
if (w->new)
3147+
continue;
3148+
3149+
if (w == last)
3150+
break;
3151+
3152+
switch (w->id) {
3153+
case snd_soc_dapm_switch:
3154+
case snd_soc_dapm_mixer:
3155+
case snd_soc_dapm_mixer_named_ctl:
3156+
for (i = 0; i < w->num_kcontrols; i++) {
3157+
struct snd_kcontrol *kcontrol = w->kcontrols[i];
3158+
struct dapm_kcontrol_data *data =
3159+
kcontrol->private_data;
3160+
struct soc_mixer_control *mc =
3161+
(struct soc_mixer_control *)
3162+
kcontrol->private_value;
3163+
3164+
if (mc->autodisable)
3165+
snd_soc_dapm_free_widget(data->widget);
3166+
}
3167+
break;
3168+
case snd_soc_dapm_demux:
3169+
case snd_soc_dapm_mux:
3170+
for (i = 0; i < w->num_kcontrols; i++) {
3171+
struct snd_kcontrol *kcontrol = w->kcontrols[i];
3172+
struct dapm_kcontrol_data *data =
3173+
kcontrol->private_data;
3174+
struct soc_enum *e = (struct soc_enum *)
3175+
kcontrol->private_value;
3176+
3177+
if (e->autodisable)
3178+
snd_soc_dapm_free_widget(data->widget);
3179+
}
3180+
break;
3181+
default:
3182+
break;
3183+
}
3184+
3185+
snd_soc_dapm_free_widget_data(w);
3186+
}
3187+
3188+
mutex_unlock(&card->dapm_mutex);
3189+
return ret;
31093190
}
31103191
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
31113192

0 commit comments

Comments
 (0)