ALSA: usb-audio: Fix a stack buffer overflow bug in check_input_term
`check_input_term` recursively calls itself with input from device side (e.g., uac_input_terminal_descriptor.bCSourceID) as argument (id). In `check_input_term`, if `check_input_term` is called with the same `id` argument as the caller, it triggers endless recursive call, resulting kernel space stack overflow. This patch fixes the bug by adding a bitmap to `struct mixer_build` to keep track of the checked ids and stop the execution if some id has been checked (similar to how parse_audio_unit handles unitid argument). Change-Id: Ibc4ab23c7d7c2fc96ff85d7cf1a1205bbd632cb5 Reported-by: Hui Peng <benquike@gmail.com> Reported-by: Mathias Payer <mathias.payer@nebelwelt.net> Signed-off-by: Hui Peng <benquike@gmail.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
8290b69077
commit
e49b967f0b
|
@ -81,6 +81,7 @@ struct mixer_build {
|
||||||
unsigned char *buffer;
|
unsigned char *buffer;
|
||||||
unsigned int buflen;
|
unsigned int buflen;
|
||||||
DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS);
|
DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS);
|
||||||
|
DECLARE_BITMAP(termbitmap, MAX_ID_ELEMS);
|
||||||
struct usb_audio_term oterm;
|
struct usb_audio_term oterm;
|
||||||
const struct usbmix_name_map *map;
|
const struct usbmix_name_map *map;
|
||||||
const struct usbmix_selector_map *selector_map;
|
const struct usbmix_selector_map *selector_map;
|
||||||
|
@ -671,14 +672,24 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
|
||||||
* parse the source unit recursively until it reaches to a terminal
|
* parse the source unit recursively until it reaches to a terminal
|
||||||
* or a branched unit.
|
* or a branched unit.
|
||||||
*/
|
*/
|
||||||
static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
|
static int __check_input_term(struct mixer_build *state, int id,
|
||||||
|
struct usb_audio_term *term)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
void *p1;
|
void *p1;
|
||||||
|
unsigned char *hdr;
|
||||||
|
|
||||||
memset(term, 0, sizeof(*term));
|
memset(term, 0, sizeof(*term));
|
||||||
while ((p1 = find_audio_control_unit(state, id)) != NULL) {
|
for (;;) {
|
||||||
unsigned char *hdr = p1;
|
/* a loop in the terminal chain? */
|
||||||
|
if (test_and_set_bit(id, state->termbitmap))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
p1 = find_audio_control_unit(state, id);
|
||||||
|
if (!p1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
hdr = p1;
|
||||||
term->id = id;
|
term->id = id;
|
||||||
switch (hdr[2]) {
|
switch (hdr[2]) {
|
||||||
case UAC_INPUT_TERMINAL:
|
case UAC_INPUT_TERMINAL:
|
||||||
|
@ -696,7 +707,7 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
|
||||||
term->name = d->iTerminal;
|
term->name = d->iTerminal;
|
||||||
|
|
||||||
/* call recursively to get the clock selectors */
|
/* call recursively to get the clock selectors */
|
||||||
err = check_input_term(state, d->bCSourceID, term);
|
err = __check_input_term(state, d->bCSourceID, term);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -719,7 +730,7 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
|
||||||
case UAC2_CLOCK_SELECTOR: {
|
case UAC2_CLOCK_SELECTOR: {
|
||||||
struct uac_selector_unit_descriptor *d = p1;
|
struct uac_selector_unit_descriptor *d = p1;
|
||||||
/* call recursively to retrieve the channel info */
|
/* call recursively to retrieve the channel info */
|
||||||
err = check_input_term(state, d->baSourceID[0], term);
|
err = __check_input_term(state, d->baSourceID[0], term);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
||||||
|
@ -767,6 +778,14 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int check_input_term(struct mixer_build *state, int id,
|
||||||
|
struct usb_audio_term *term)
|
||||||
|
{
|
||||||
|
memset(term, 0, sizeof(*term));
|
||||||
|
memset(state->termbitmap, 0, sizeof(state->termbitmap));
|
||||||
|
return __check_input_term(state, id, term);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Feature Unit
|
* Feature Unit
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue