On Thu, 15 Aug 2019 19:19:10 +0200, Hui Peng wrote: > > Hi, Takashi: > > One point I want to be clear: if an endless recursive loop is detected, should > we return 0, or a negative error code? An error might be more appropriate, but it's no big deal, as you'll likely hit other errors sooner or later at parsing further :) thanks, Takashi > On Thu, Aug 15, 2019 at 2:58 AM Takashi Iwai <tiwai@xxxxxxx> wrote: > > On Thu, 15 Aug 2019 08:13:57 +0200, > Takashi Iwai wrote: > > > > On Thu, 15 Aug 2019 06:35:49 +0200, > > Hui Peng wrote: > > > > > > `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 by `check_input_term` and stop > > > the execution if some id has been checked (similar to how > > > parse_audio_unit handles unitid argument). > > > > > > Reported-by: Hui Peng <benquike@xxxxxxxxx> > > > Reported-by: Mathias Payer <mathias.payer@xxxxxxxxxxxxx> > > > Signed-off-by: Hui Peng <benquike@xxxxxxxxx> > > > > The fix looks almost good, but we need to be careful about the > > bitmap check. In theory, it's possible that multiple nodes point to > > the same input terminal, and your patch would break that scenario. > > For fixing that, we need to zero-clear the termbitmap at each first > > invocation of check_input_term(), something like below. > > > > Could you check whether this works? > > Thinking of this further, there is another possible infinite loop. > Namely, when the feature unit in the input terminal chain points to > itself as the source, it'll loop endlessly without the stack > overflow. > > So the check of the termbitmap should be inside the loop. > The revised patch is below. > > thanks, > > Takashi > > -- 8< -- > From: Hui Peng <benquike@xxxxxxxxx> > Subject: [PATCH] ALSA: usb-audio: Fix a stack buffer overflow bug > 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 by `check_input_term` and stop > the execution if some id has been checked (similar to how > parse_audio_unit handles unitid argument). > > [ The termbitmap needs to be cleared at each first check of the input > terminal, so the function got split now. Also, for catching another > endless loop in the input terminal chain -- where the feature unit > points to itself as its source -- the termbitmap check is moved > inside the parser loop. -- tiwai ] > > Reported-by: Hui Peng <benquike@xxxxxxxxx> > Reported-by: Mathias Payer <mathias.payer@xxxxxxxxxxxxx> > Signed-off-by: Hui Peng <benquike@xxxxxxxxx> > Cc: <stable@xxxxxxxxxxxxxxx> > Signed-off-by: Takashi Iwai <tiwai@xxxxxxx> > --- > sound/usb/mixer.c | 36 ++++++++++++++++++++++++++---------- > 1 file changed, 26 insertions(+), 10 deletions(-) > > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index ea487378be17..aa8b046aa91f 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -68,6 +68,7 @@ struct mixer_build { > unsigned char *buffer; > unsigned int buflen; > DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS); > + DECLARE_BITMAP(termbitmap, MAX_ID_ELEMS); > struct usb_audio_term oterm; > const struct usbmix_name_map *map; > const struct usbmix_selector_map *selector_map; > @@ -775,16 +776,23 @@ static int uac_mixer_unit_get_channels(struct > mixer_build *state, > * parse the source unit recursively until it reaches to a terminal > * 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 protocol = state->mixer->protocol; > int err; > void *p1; > + unsigned char *hdr; > > - memset(term, 0, sizeof(*term)); > - while ((p1 = find_audio_control_unit(state, id)) != NULL) { > - unsigned char *hdr = p1; > + for (;;) { > + /* a loop in the terminal chain? */ > + if (test_and_set_bit(id, state->termbitmap)) > + break; > + > + p1 = find_audio_control_unit(state, id); > + if (!p1) > + break; > + hdr = p1; > term->id = id; > > if (protocol == UAC_VERSION_1 || protocol == > UAC_VERSION_2) { > @@ -802,7 +810,7 @@ static int check_input_term(struct mixer_build *state, > int id, > > /* call recursively to verify that > the > * referenced clock entity is > valid */ > - err = check_input_term(state, d-> > bCSourceID, term); > + err = __check_input_term(state, > d->bCSourceID, term); > if (err < 0) > return err; > > @@ -836,7 +844,7 @@ static int check_input_term(struct mixer_build *state, > int id, > case UAC2_CLOCK_SELECTOR: { > struct uac_selector_unit_descriptor *d = > p1; > /* 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) > return err; > term->type = UAC3_SELECTOR_UNIT << 16; /* > virtual type */ > @@ -899,7 +907,7 @@ static int check_input_term(struct mixer_build *state, > int id, > > /* call recursively to verify that the > * referenced clock entity is valid */ > - err = check_input_term(state, d-> > bCSourceID, term); > + err = __check_input_term(state, d-> > bCSourceID, term); > if (err < 0) > return err; > > @@ -950,7 +958,7 @@ static int check_input_term(struct mixer_build *state, > int id, > case UAC3_CLOCK_SELECTOR: { > struct uac_selector_unit_descriptor *d = > p1; > /* 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) > return err; > term->type = UAC3_SELECTOR_UNIT << 16; /* > virtual type */ > @@ -966,7 +974,7 @@ static int check_input_term(struct mixer_build *state, > int id, > return -EINVAL; > > /* 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) > return err; > > @@ -984,6 +992,14 @@ static int check_input_term(struct mixer_build > *state, int id, > return -ENODEV; > } > > +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 > */ > -- > 2.16.4 > > _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx https://mailman.alsa-project.org/mailman/listinfo/alsa-devel