[alsa-devel] [PATCH] Fix a stack buffer overflow bug check_input_term

Hui Peng benquike at gmail.com
Thu Aug 15 20:11:50 CEST 2019


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Hi, Takashi:

The new patch is confirmed (I made it to return -EINVAL if a endless
recursive call is detected).
Can you have a look.

Thanks.

On 8/15/19 1:38 PM, Takashi Iwai wrote:
> 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 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
>> > > Reported-by: Mathias Payer
>> > > Signed-off-by: Hui Peng
>> >
>> > 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
>> 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
>> Reported-by: Mathias Payer
>> Signed-off-by: Hui Peng
>> Cc:
>> Signed-off-by: Takashi Iwai
>> ---
>>  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
>>
>>
-----BEGIN PGP SIGNATURE-----

iQEzBAEBCgAdFiEE1BqveDE4xg/g0AwHIp/nqknaPR4FAl1VoGYACgkQIp/nqkna
PR7begf/VLHveyoRopqc2MMhJ7aSH9837dNvss2JV/QVvUrdRvpKDpLNLx9EkxSu
U8FXWTl7HImaDBszTwtOJG8Peh/6L8G3ouAtiFIMhq9AsLqMOKS2p3wIvkJwGiCM
hjSZ3U7A8jaIjdUnUz2bVMvLVLfZH7dI8kIUuKtqh7qtBBnRL6w2RhfO1GdMnxvU
etczHfl4anKuQbfMZpI9Xv1ruFkYewUQOBhK4Kp/De00GqqtaINm73WYVqY3gf6I
Txk8zrLBsgFk3wJI6qi1NeITiZ4z8kd7wJL84rj8PraqtFpmkn7p7QfVzDSLibvP
V2HZfnaVwXrAf/FZrxYjpqfoZH44JA==
=MKbq
-----END PGP SIGNATURE-----
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Fix-a-stack-buffer-overflow-bug-in-check_input_term.patch
Type: text/x-patch
Size: 4555 bytes
Desc: not available
URL: <http://mailman.alsa-project.org/pipermail/alsa-devel/attachments/20190815/af3bd6fb/attachment.bin>


More information about the Alsa-devel mailing list