Commit e2810d76 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: usb-audio: Fix memory leak and corruption in mixer_us16x08.c

There are a few places leaking memory and doing double-free in
mixer_us16x08.c.

The driver allocates a usb_mixer_elem_info object at each
add_new_ctl() call.  This has to be freed via kctl->private_free, but
currently this is done properly only for some controls.

Also, the driver allocates three external objects (comp_store,
eq_store, meter_store), and these are referred in elem->private_data
(it's not kctl->private_data).  And these have to be released, but
there are none doing it.  Moreover, these extra objects have to be
released only once.  Thus the release should be done only by the first
kctl element that refers to it.

For fixing these, we call either snd_usb_mixer_elem_free() (only for
kctl->private_data) or elem_private_free() (for both
kctl->private_data and elem->private_data) via kctl->private_free
appropriately.

Last but not least, snd_us16x08_controls_create() may return in the
middle without releasing the allocated *_store objects due to an
error.  For fixing this, we shuffle the allocation code so that it's
called just before its reference.

Fixes: d2bb390a ("ALSA: usb-audio: Tascam US-16x08 DSP mixer quirk")
Reported-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Reviewed-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 89b593c3
...@@ -1053,11 +1053,22 @@ static struct snd_us16x08_meter_store *snd_us16x08_create_meter_store(void) ...@@ -1053,11 +1053,22 @@ static struct snd_us16x08_meter_store *snd_us16x08_create_meter_store(void)
} }
/* release elem->private_free as well; called only once for each *_store */
static void elem_private_free(struct snd_kcontrol *kctl)
{
struct usb_mixer_elem_info *elem = kctl->private_data;
if (elem)
kfree(elem->private_data);
kfree(elem);
kctl->private_data = NULL;
}
static int add_new_ctl(struct usb_mixer_interface *mixer, static int add_new_ctl(struct usb_mixer_interface *mixer,
const struct snd_kcontrol_new *ncontrol, const struct snd_kcontrol_new *ncontrol,
int index, int val_type, int channels, int index, int val_type, int channels,
const char *name, const void *opt, const char *name, const void *opt,
void (*freeer)(struct snd_kcontrol *kctl), bool do_private_free,
struct usb_mixer_elem_info **elem_ret) struct usb_mixer_elem_info **elem_ret)
{ {
struct snd_kcontrol *kctl; struct snd_kcontrol *kctl;
...@@ -1085,7 +1096,10 @@ static int add_new_ctl(struct usb_mixer_interface *mixer, ...@@ -1085,7 +1096,10 @@ static int add_new_ctl(struct usb_mixer_interface *mixer,
return -ENOMEM; return -ENOMEM;
} }
kctl->private_free = freeer; if (do_private_free)
kctl->private_free = elem_private_free;
else
kctl->private_free = snd_usb_mixer_elem_free;
strlcpy(kctl->id.name, name, sizeof(kctl->id.name)); strlcpy(kctl->id.name, name, sizeof(kctl->id.name));
...@@ -1109,7 +1123,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1109,7 +1123,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_BOOLEAN, .type = USB_MIXER_BOOLEAN,
.num_channels = 16, .num_channels = 16,
.name = "EQ Switch", .name = "EQ Switch",
.freeer = snd_usb_mixer_elem_free
}, },
{ /* EQ low gain */ { /* EQ low gain */
.kcontrol_new = &snd_us16x08_eq_gain_ctl, .kcontrol_new = &snd_us16x08_eq_gain_ctl,
...@@ -1117,7 +1130,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1117,7 +1130,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ Low Volume", .name = "EQ Low Volume",
.freeer = snd_usb_mixer_elem_free
}, },
{ /* EQ low freq */ { /* EQ low freq */
.kcontrol_new = &snd_us16x08_eq_low_freq_ctl, .kcontrol_new = &snd_us16x08_eq_low_freq_ctl,
...@@ -1125,7 +1137,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1125,7 +1137,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ Low Frequence", .name = "EQ Low Frequence",
.freeer = NULL
}, },
{ /* EQ mid low gain */ { /* EQ mid low gain */
.kcontrol_new = &snd_us16x08_eq_gain_ctl, .kcontrol_new = &snd_us16x08_eq_gain_ctl,
...@@ -1133,7 +1144,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1133,7 +1144,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ MidLow Volume", .name = "EQ MidLow Volume",
.freeer = snd_usb_mixer_elem_free
}, },
{ /* EQ mid low freq */ { /* EQ mid low freq */
.kcontrol_new = &snd_us16x08_eq_mid_freq_ctl, .kcontrol_new = &snd_us16x08_eq_mid_freq_ctl,
...@@ -1141,7 +1151,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1141,7 +1151,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ MidLow Frequence", .name = "EQ MidLow Frequence",
.freeer = NULL
}, },
{ /* EQ mid low Q */ { /* EQ mid low Q */
.kcontrol_new = &snd_us16x08_eq_mid_width_ctl, .kcontrol_new = &snd_us16x08_eq_mid_width_ctl,
...@@ -1149,7 +1158,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1149,7 +1158,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ MidQLow Q", .name = "EQ MidQLow Q",
.freeer = NULL
}, },
{ /* EQ mid high gain */ { /* EQ mid high gain */
.kcontrol_new = &snd_us16x08_eq_gain_ctl, .kcontrol_new = &snd_us16x08_eq_gain_ctl,
...@@ -1157,7 +1165,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1157,7 +1165,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ MidHigh Volume", .name = "EQ MidHigh Volume",
.freeer = snd_usb_mixer_elem_free
}, },
{ /* EQ mid high freq */ { /* EQ mid high freq */
.kcontrol_new = &snd_us16x08_eq_mid_freq_ctl, .kcontrol_new = &snd_us16x08_eq_mid_freq_ctl,
...@@ -1165,7 +1172,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1165,7 +1172,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ MidHigh Frequence", .name = "EQ MidHigh Frequence",
.freeer = NULL
}, },
{ /* EQ mid high Q */ { /* EQ mid high Q */
.kcontrol_new = &snd_us16x08_eq_mid_width_ctl, .kcontrol_new = &snd_us16x08_eq_mid_width_ctl,
...@@ -1173,7 +1179,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1173,7 +1179,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ MidHigh Q", .name = "EQ MidHigh Q",
.freeer = NULL
}, },
{ /* EQ high gain */ { /* EQ high gain */
.kcontrol_new = &snd_us16x08_eq_gain_ctl, .kcontrol_new = &snd_us16x08_eq_gain_ctl,
...@@ -1181,7 +1186,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1181,7 +1186,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ High Volume", .name = "EQ High Volume",
.freeer = snd_usb_mixer_elem_free
}, },
{ /* EQ low freq */ { /* EQ low freq */
.kcontrol_new = &snd_us16x08_eq_high_freq_ctl, .kcontrol_new = &snd_us16x08_eq_high_freq_ctl,
...@@ -1189,7 +1193,6 @@ static struct snd_us16x08_control_params eq_controls[] = { ...@@ -1189,7 +1193,6 @@ static struct snd_us16x08_control_params eq_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "EQ High Frequence", .name = "EQ High Frequence",
.freeer = NULL
}, },
}; };
...@@ -1201,7 +1204,6 @@ static struct snd_us16x08_control_params comp_controls[] = { ...@@ -1201,7 +1204,6 @@ static struct snd_us16x08_control_params comp_controls[] = {
.type = USB_MIXER_BOOLEAN, .type = USB_MIXER_BOOLEAN,
.num_channels = 16, .num_channels = 16,
.name = "Compressor Switch", .name = "Compressor Switch",
.freeer = snd_usb_mixer_elem_free
}, },
{ /* Comp threshold */ { /* Comp threshold */
.kcontrol_new = &snd_us16x08_comp_threshold_ctl, .kcontrol_new = &snd_us16x08_comp_threshold_ctl,
...@@ -1209,7 +1211,6 @@ static struct snd_us16x08_control_params comp_controls[] = { ...@@ -1209,7 +1211,6 @@ static struct snd_us16x08_control_params comp_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "Compressor Threshold Volume", .name = "Compressor Threshold Volume",
.freeer = NULL
}, },
{ /* Comp ratio */ { /* Comp ratio */
.kcontrol_new = &snd_us16x08_comp_ratio_ctl, .kcontrol_new = &snd_us16x08_comp_ratio_ctl,
...@@ -1217,7 +1218,6 @@ static struct snd_us16x08_control_params comp_controls[] = { ...@@ -1217,7 +1218,6 @@ static struct snd_us16x08_control_params comp_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "Compressor Ratio", .name = "Compressor Ratio",
.freeer = NULL
}, },
{ /* Comp attack */ { /* Comp attack */
.kcontrol_new = &snd_us16x08_comp_attack_ctl, .kcontrol_new = &snd_us16x08_comp_attack_ctl,
...@@ -1225,7 +1225,6 @@ static struct snd_us16x08_control_params comp_controls[] = { ...@@ -1225,7 +1225,6 @@ static struct snd_us16x08_control_params comp_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "Compressor Attack", .name = "Compressor Attack",
.freeer = NULL
}, },
{ /* Comp release */ { /* Comp release */
.kcontrol_new = &snd_us16x08_comp_release_ctl, .kcontrol_new = &snd_us16x08_comp_release_ctl,
...@@ -1233,7 +1232,6 @@ static struct snd_us16x08_control_params comp_controls[] = { ...@@ -1233,7 +1232,6 @@ static struct snd_us16x08_control_params comp_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "Compressor Release", .name = "Compressor Release",
.freeer = NULL
}, },
{ /* Comp gain */ { /* Comp gain */
.kcontrol_new = &snd_us16x08_comp_gain_ctl, .kcontrol_new = &snd_us16x08_comp_gain_ctl,
...@@ -1241,7 +1239,6 @@ static struct snd_us16x08_control_params comp_controls[] = { ...@@ -1241,7 +1239,6 @@ static struct snd_us16x08_control_params comp_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "Compressor Volume", .name = "Compressor Volume",
.freeer = NULL
}, },
}; };
...@@ -1253,7 +1250,6 @@ static struct snd_us16x08_control_params channel_controls[] = { ...@@ -1253,7 +1250,6 @@ static struct snd_us16x08_control_params channel_controls[] = {
.type = USB_MIXER_BOOLEAN, .type = USB_MIXER_BOOLEAN,
.num_channels = 16, .num_channels = 16,
.name = "Phase Switch", .name = "Phase Switch",
.freeer = snd_usb_mixer_elem_free,
.default_val = 0 .default_val = 0
}, },
{ /* Fader */ { /* Fader */
...@@ -1262,7 +1258,6 @@ static struct snd_us16x08_control_params channel_controls[] = { ...@@ -1262,7 +1258,6 @@ static struct snd_us16x08_control_params channel_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "Line Volume", .name = "Line Volume",
.freeer = NULL,
.default_val = 127 .default_val = 127
}, },
{ /* Mute */ { /* Mute */
...@@ -1271,7 +1266,6 @@ static struct snd_us16x08_control_params channel_controls[] = { ...@@ -1271,7 +1266,6 @@ static struct snd_us16x08_control_params channel_controls[] = {
.type = USB_MIXER_BOOLEAN, .type = USB_MIXER_BOOLEAN,
.num_channels = 16, .num_channels = 16,
.name = "Mute Switch", .name = "Mute Switch",
.freeer = NULL,
.default_val = 0 .default_val = 0
}, },
{ /* Pan */ { /* Pan */
...@@ -1280,7 +1274,6 @@ static struct snd_us16x08_control_params channel_controls[] = { ...@@ -1280,7 +1274,6 @@ static struct snd_us16x08_control_params channel_controls[] = {
.type = USB_MIXER_U16, .type = USB_MIXER_U16,
.num_channels = 16, .num_channels = 16,
.name = "Pan Left-Right Volume", .name = "Pan Left-Right Volume",
.freeer = NULL,
.default_val = 127 .default_val = 127
}, },
}; };
...@@ -1293,7 +1286,6 @@ static struct snd_us16x08_control_params master_controls[] = { ...@@ -1293,7 +1286,6 @@ static struct snd_us16x08_control_params master_controls[] = {
.type = USB_MIXER_U8, .type = USB_MIXER_U8,
.num_channels = 16, .num_channels = 16,
.name = "Master Volume", .name = "Master Volume",
.freeer = NULL,
.default_val = 127 .default_val = 127
}, },
{ /* Bypass */ { /* Bypass */
...@@ -1302,7 +1294,6 @@ static struct snd_us16x08_control_params master_controls[] = { ...@@ -1302,7 +1294,6 @@ static struct snd_us16x08_control_params master_controls[] = {
.type = USB_MIXER_BOOLEAN, .type = USB_MIXER_BOOLEAN,
.num_channels = 16, .num_channels = 16,
.name = "DSP Bypass Switch", .name = "DSP Bypass Switch",
.freeer = NULL,
.default_val = 0 .default_val = 0
}, },
{ /* Buss out */ { /* Buss out */
...@@ -1311,7 +1302,6 @@ static struct snd_us16x08_control_params master_controls[] = { ...@@ -1311,7 +1302,6 @@ static struct snd_us16x08_control_params master_controls[] = {
.type = USB_MIXER_BOOLEAN, .type = USB_MIXER_BOOLEAN,
.num_channels = 16, .num_channels = 16,
.name = "Buss Out Switch", .name = "Buss Out Switch",
.freeer = NULL,
.default_val = 0 .default_val = 0
}, },
{ /* Master mute */ { /* Master mute */
...@@ -1320,7 +1310,6 @@ static struct snd_us16x08_control_params master_controls[] = { ...@@ -1320,7 +1310,6 @@ static struct snd_us16x08_control_params master_controls[] = {
.type = USB_MIXER_BOOLEAN, .type = USB_MIXER_BOOLEAN,
.num_channels = 16, .num_channels = 16,
.name = "Master Mute Switch", .name = "Master Mute Switch",
.freeer = NULL,
.default_val = 0 .default_val = 0
}, },
...@@ -1338,30 +1327,10 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) ...@@ -1338,30 +1327,10 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
/* just check for non-MIDI interface */ /* just check for non-MIDI interface */
if (mixer->hostif->desc.bInterfaceNumber == 3) { if (mixer->hostif->desc.bInterfaceNumber == 3) {
/* create compressor mixer elements */
comp_store = snd_us16x08_create_comp_store();
if (comp_store == NULL)
return -ENOMEM;
/* create eq store */
eq_store = snd_us16x08_create_eq_store();
if (eq_store == NULL) {
kfree(comp_store);
return -ENOMEM;
}
/* create meters store */
meter_store = snd_us16x08_create_meter_store();
if (meter_store == NULL) {
kfree(comp_store);
kfree(eq_store);
return -ENOMEM;
}
/* add routing control */ /* add routing control */
err = add_new_ctl(mixer, &snd_us16x08_route_ctl, err = add_new_ctl(mixer, &snd_us16x08_route_ctl,
SND_US16X08_ID_ROUTE, USB_MIXER_U8, 8, "Line Out Route", SND_US16X08_ID_ROUTE, USB_MIXER_U8, 8, "Line Out Route",
NULL, NULL, &elem); NULL, false, &elem);
if (err < 0) { if (err < 0) {
usb_audio_dbg(mixer->chip, usb_audio_dbg(mixer->chip,
"Failed to create route control, err:%d\n", "Failed to create route control, err:%d\n",
...@@ -1372,6 +1341,11 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) ...@@ -1372,6 +1341,11 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
elem->cache_val[i] = i < 2 ? i : i + 2; elem->cache_val[i] = i < 2 ? i : i + 2;
elem->cached = 0xff; elem->cached = 0xff;
/* create compressor mixer elements */
comp_store = snd_us16x08_create_comp_store();
if (!comp_store)
return -ENOMEM;
/* add master controls */ /* add master controls */
for (i = 0; for (i = 0;
i < sizeof(master_controls) i < sizeof(master_controls)
...@@ -1385,7 +1359,8 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) ...@@ -1385,7 +1359,8 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
master_controls[i].num_channels, master_controls[i].num_channels,
master_controls[i].name, master_controls[i].name,
comp_store, comp_store,
master_controls[i].freeer, &elem); i == 0, /* release comp_store only once */
&elem);
if (err < 0) if (err < 0)
return err; return err;
elem->cache_val[0] = master_controls[i].default_val; elem->cache_val[0] = master_controls[i].default_val;
...@@ -1405,7 +1380,7 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) ...@@ -1405,7 +1380,7 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
channel_controls[i].num_channels, channel_controls[i].num_channels,
channel_controls[i].name, channel_controls[i].name,
comp_store, comp_store,
channel_controls[i].freeer, &elem); false, &elem);
if (err < 0) if (err < 0)
return err; return err;
for (j = 0; j < SND_US16X08_MAX_CHANNELS; j++) { for (j = 0; j < SND_US16X08_MAX_CHANNELS; j++) {
...@@ -1415,6 +1390,11 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) ...@@ -1415,6 +1390,11 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
elem->cached = 0xffff; elem->cached = 0xffff;
} }
/* create eq store */
eq_store = snd_us16x08_create_eq_store();
if (!eq_store)
return -ENOMEM;
/* add EQ controls */ /* add EQ controls */
for (i = 0; i < sizeof(eq_controls) / for (i = 0; i < sizeof(eq_controls) /
sizeof(control_params); i++) { sizeof(control_params); i++) {
...@@ -1426,7 +1406,8 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) ...@@ -1426,7 +1406,8 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
eq_controls[i].num_channels, eq_controls[i].num_channels,
eq_controls[i].name, eq_controls[i].name,
eq_store, eq_store,
eq_controls[i].freeer, NULL); i == 0, /* release eq_store only once */
NULL);
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -1444,18 +1425,23 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) ...@@ -1444,18 +1425,23 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
comp_controls[i].num_channels, comp_controls[i].num_channels,
comp_controls[i].name, comp_controls[i].name,
comp_store, comp_store,
comp_controls[i].freeer, NULL); false, NULL);
if (err < 0) if (err < 0)
return err; return err;
} }
/* create meters store */
meter_store = snd_us16x08_create_meter_store();
if (!meter_store)
return -ENOMEM;
/* meter function 'get' must access to compressor store /* meter function 'get' must access to compressor store
* so place a reference here * so place a reference here
*/ */
meter_store->comp_store = comp_store; meter_store->comp_store = comp_store;
err = add_new_ctl(mixer, &snd_us16x08_meter_ctl, err = add_new_ctl(mixer, &snd_us16x08_meter_ctl,
SND_US16X08_ID_METER, USB_MIXER_U16, 0, "Level Meter", SND_US16X08_ID_METER, USB_MIXER_U16, 0, "Level Meter",
(void *) meter_store, snd_usb_mixer_elem_free, NULL); meter_store, true, NULL);
if (err < 0) if (err < 0)
return err; return err;
} }
......
...@@ -112,7 +112,6 @@ struct snd_us16x08_control_params { ...@@ -112,7 +112,6 @@ struct snd_us16x08_control_params {
int type; int type;
int num_channels; int num_channels;
const char *name; const char *name;
void (*freeer)(struct snd_kcontrol *kctl);
int default_val; int default_val;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment