ALSA: usb-audio: Fix invalid UAC2/3 mixer unit matrix evaluation

The bitmap matrix in the mixer unit descriptor for UAC2 and UAC3 has
rather the size of input-pins x output-pins, while the current
USB-audio driver code wrongly assumes the UAC1 bitmap matrix size,
which is input-channels x output-pins.  That is, when input pins have
multiple channels, the column size differs and it leads to the
accesses at a wrong position.

This patch corrects the access of the bitmap matrix for UAC2/UAC3.
For making the code cleaner, split the parser to UAC1 and UAC2/3, too.

Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 23caaf19b1 ("ALSA: usb-mixer: Add support for Audio Class v2.0")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20261007172051.13240-5-tiwai@suse.de
This commit is contained in:
Takashi Iwai 2026-10-07 19:20:40 +02:00
parent 27cee4141c
commit c01fb2ed90

View File

@ -2377,7 +2377,7 @@ static bool mixer_bitmap_overflow(struct uac_mixer_unit_descriptor *desc,
*/
static void build_mixer_unit_ctl(struct mixer_build *state,
struct uac_mixer_unit_descriptor *desc,
int in_pin, int in_ch, int num_outs,
int in_ch, int num_outs,
int unitid, struct usb_audio_term *iterm)
{
struct usb_mixer_elem_info *cval;
@ -2472,34 +2472,13 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
/*
* parse a mixer unit
*/
static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
void *raw_desc)
/* UAC1 mixer unit */
static int parse_audio_mixer_unit_v1(struct mixer_build *state, int unitid,
int input_pins, int num_outs,
struct uac_mixer_unit_descriptor *desc)
{
struct uac_mixer_unit_descriptor *desc = raw_desc;
struct usb_audio_term iterm;
int input_pins, num_ins, num_outs;
int pin, ich, err;
err = uac_mixer_unit_get_channels(state, desc);
if (err < 0) {
usb_audio_err(state->chip,
"invalid MIXER UNIT descriptor %d\n",
unitid);
return err;
}
num_outs = err;
input_pins = desc->bNrInPins;
if (state->mixer->protocol == UAC_VERSION_2 ||
state->mixer->protocol == UAC_VERSION_3) {
if (input_pins * num_outs > 256) {
usb_audio_err(state->chip,
"invalid channels for MIXER UNIT %d: input=%d, output=%d\n",
unitid, input_pins, num_outs);
return -EINVAL;
}
}
int num_ins, pin, ich, och, err;
num_ins = 0;
ich = 0;
@ -2518,25 +2497,90 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
num_ins, num_outs))
break;
for (; ich < num_ins; ich++) {
int och, ich_has_controls = 0;
for (och = 0; och < num_outs; och++) {
__u8 *c = uac_mixer_unit_bmControls(desc,
state->mixer->protocol);
if (check_matrix_bitmap(c, ich, och, num_outs)) {
ich_has_controls = 1;
if (check_matrix_bitmap(c, ich, och, num_outs))
break;
}
}
if (ich_has_controls)
build_mixer_unit_ctl(state, desc, pin, ich, num_outs,
if (och < num_outs)
build_mixer_unit_ctl(state, desc, ich, num_outs,
unitid, &iterm);
}
}
return 0;
}
/* UAC2/UAC3 mixer unit */
static int parse_audio_mixer_unit_v2(struct mixer_build *state, int unitid,
int input_pins, int num_outs,
struct uac_mixer_unit_descriptor *desc)
{
struct usb_audio_term iterm;
int pin, och, err;
if (input_pins * num_outs > 256 ||
mixer_bitmap_overflow(desc, state->mixer->protocol,
input_pins, num_outs)) {
usb_audio_err(state->chip,
"invalid channels for MIXER UNIT %d: input=%d, output=%d\n",
unitid, input_pins, num_outs);
return -EINVAL;
}
for (pin = 0; pin < input_pins; pin++) {
err = parse_audio_unit(state, desc->baSourceID[pin]);
if (err < 0)
continue;
if (!num_outs)
continue;
err = check_input_term(state, desc->baSourceID[pin], &iterm);
if (err < 0)
return err;
for (och = 0; och < num_outs; och++) {
__u8 *c = uac_mixer_unit_bmControls(desc,
state->mixer->protocol);
if (check_matrix_bitmap(c, pin, och, num_outs))
break;
}
if (och < num_outs)
build_mixer_unit_ctl(state, desc, pin, num_outs,
unitid, &iterm);
}
return 0;
}
static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
void *raw_desc)
{
struct uac_mixer_unit_descriptor *desc = raw_desc;
int num_outs;
num_outs = uac_mixer_unit_get_channels(state, desc);
if (num_outs < 0) {
usb_audio_err(state->chip,
"invalid MIXER UNIT descriptor %d\n",
unitid);
return num_outs;
}
switch (state->mixer->protocol) {
case UAC_VERSION_1:
default:
return parse_audio_mixer_unit_v1(state, unitid,
desc->bNrInPins, num_outs,
desc);
case UAC_VERSION_2:
case UAC_VERSION_3:
return parse_audio_mixer_unit_v2(state, unitid,
desc->bNrInPins, num_outs,
desc);
}
}
/*
* Processing Unit / Extension Unit
*/