Skip to content

Commit 172811e

Browse files
rfvirgiltiwai
authored andcommitted
ALSA: hda/cs_dsp_ctl: Use private_free for control cleanup
Use the control private_free callback to free the associated data block. This ensures that the memory won't leak, whatever way the control gets destroyed. The original implementation didn't actually remove the ALSA controls in hda_cs_dsp_control_remove(). It only freed the internal tracking structure. This meant it was possible to remove/unload the amp driver while leaving its ALSA controls still present in the soundcard. Obviously attempting to access them could cause segfaults or at least dereferencing stale pointers. Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com> Fixes: 3233b97 ("ALSA: hda: hda_cs_dsp_ctl: Add Library to support CS_DSP ALSA controls") Link: https://lore.kernel.org/r/20240508095627.44476-1-rf@opensource.cirrus.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent b7df4cc commit 172811e

1 file changed

Lines changed: 31 additions & 16 deletions

File tree

sound/pci/hda/hda_cs_dsp_ctl.c

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <linux/module.h>
1010
#include <sound/soc.h>
11+
#include <linux/cleanup.h>
1112
#include <linux/firmware/cirrus/cs_dsp.h>
1213
#include <linux/firmware/cirrus/wmfw.h>
1314
#include "hda_cs_dsp_ctl.h"
@@ -97,11 +98,23 @@ static unsigned int wmfw_convert_flags(unsigned int in)
9798
return out;
9899
}
99100

100-
static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char *name)
101+
static void hda_cs_dsp_free_kcontrol(struct snd_kcontrol *kctl)
101102
{
103+
struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl);
102104
struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
105+
106+
/* NULL priv to prevent a double-free in hda_cs_dsp_control_remove() */
107+
cs_ctl->priv = NULL;
108+
kfree(ctl);
109+
}
110+
111+
static void hda_cs_dsp_add_kcontrol(struct cs_dsp_coeff_ctl *cs_ctl,
112+
const struct hda_cs_dsp_ctl_info *info,
113+
const char *name)
114+
{
103115
struct snd_kcontrol_new kcontrol = {0};
104116
struct snd_kcontrol *kctl;
117+
struct hda_cs_dsp_coeff_ctl *ctl __free(kfree) = NULL;
105118
int ret = 0;
106119

107120
if (cs_ctl->len > ADSP_MAX_STD_CTRL_SIZE) {
@@ -110,35 +123,43 @@ static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char
110123
return;
111124
}
112125

126+
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
127+
if (!ctl)
128+
return;
129+
130+
ctl->cs_ctl = cs_ctl;
131+
ctl->card = info->card;
132+
113133
kcontrol.name = name;
114134
kcontrol.info = hda_cs_dsp_coeff_info;
115135
kcontrol.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
116136
kcontrol.access = wmfw_convert_flags(cs_ctl->flags);
117137
kcontrol.get = hda_cs_dsp_coeff_get;
118138
kcontrol.put = hda_cs_dsp_coeff_put;
119139

120-
/* Save ctl inside private_data, ctl is owned by cs_dsp,
121-
* and will be freed when cs_dsp removes the control */
122140
kctl = snd_ctl_new1(&kcontrol, (void *)ctl);
123141
if (!kctl)
124142
return;
125143

126-
ret = snd_ctl_add(ctl->card, kctl);
144+
kctl->private_free = hda_cs_dsp_free_kcontrol;
145+
ctl->kctl = kctl;
146+
147+
/* snd_ctl_add() calls our private_free on error, which will kfree(ctl) */
148+
cs_ctl->priv = no_free_ptr(ctl);
149+
ret = snd_ctl_add(info->card, kctl);
127150
if (ret) {
128151
dev_err(cs_ctl->dsp->dev, "Failed to add KControl %s = %d\n", kcontrol.name, ret);
129152
return;
130153
}
131154

132155
dev_dbg(cs_ctl->dsp->dev, "Added KControl: %s\n", kcontrol.name);
133-
ctl->kctl = kctl;
134156
}
135157

136158
static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl,
137159
const struct hda_cs_dsp_ctl_info *info)
138160
{
139161
struct cs_dsp *cs_dsp = cs_ctl->dsp;
140162
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
141-
struct hda_cs_dsp_coeff_ctl *ctl;
142163
const char *region_name;
143164
int ret;
144165

@@ -163,15 +184,7 @@ static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl,
163184
" %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
164185
}
165186

166-
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
167-
if (!ctl)
168-
return;
169-
170-
ctl->cs_ctl = cs_ctl;
171-
ctl->card = info->card;
172-
cs_ctl->priv = ctl;
173-
174-
hda_cs_dsp_add_kcontrol(ctl, name);
187+
hda_cs_dsp_add_kcontrol(cs_ctl, info, name);
175188
}
176189

177190
void hda_cs_dsp_add_controls(struct cs_dsp *dsp, const struct hda_cs_dsp_ctl_info *info)
@@ -203,7 +216,9 @@ void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
203216
{
204217
struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv;
205218

206-
kfree(ctl);
219+
/* ctl and kctl may already have been removed by ALSA private_free */
220+
if (ctl && ctl->kctl)
221+
snd_ctl_remove(ctl->card, ctl->kctl);
207222
}
208223
EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS);
209224

0 commit comments

Comments
 (0)