linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Takashi Iwai <tiwai@suse.de>
To: Hui Peng <benquike@gmail.com>
Cc: security@kernel.org, alsa-devel@alsa-project.org,
	YueHaibing <yuehaibing@huawei.com>,
	Thomas Gleixner <tglx@linutronix.de>,
	Allison Randal <allison@lohutok.net>,
	Mathias Payer <mathias.payer@nebelwelt.net>,
	Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.com>,
	Wenwen Wang <wang6495@umn.edu>,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH] Fix a stack buffer overflow bug check_input_term
Date: Thu, 15 Aug 2019 19:38:27 +0200	[thread overview]
Message-ID: <s5h7e7e9vn0.wl-tiwai@suse.de> (raw)
In-Reply-To: <CAKpmkkWCaLOctG44fD=arD-=oogRVCSxe5rz2UNUAms5q=2pYw@mail.gmail.com>

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@suse.de> 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@gmail.com>
>     > > Reported-by: Mathias Payer <mathias.payer@nebelwelt.net>
>     > > Signed-off-by: Hui Peng <benquike@gmail.com>
>     >
>     > 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@gmail.com>
>     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@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>
>     ---
>      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
> 
> 

  parent reply	other threads:[~2019-08-15 17:38 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-08-15  4:35 [PATCH] Fix a stack buffer overflow bug check_input_term Hui Peng
2019-08-15  4:47 ` Hui Peng
2019-08-15  6:13 ` Takashi Iwai
2019-08-15  6:58   ` Takashi Iwai
     [not found]     ` <CAKpmkkWCaLOctG44fD=arD-=oogRVCSxe5rz2UNUAms5q=2pYw@mail.gmail.com>
2019-08-15 17:38       ` Takashi Iwai [this message]
     [not found]         ` <c6b11a34-3bd9-f1d7-cd27-4ddcd210a7cc@gmail.com>
2019-08-15 20:16           ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=s5h7e7e9vn0.wl-tiwai@suse.de \
    --to=tiwai@suse.de \
    --cc=allison@lohutok.net \
    --cc=alsa-devel@alsa-project.org \
    --cc=benquike@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mathias.payer@nebelwelt.net \
    --cc=perex@perex.cz \
    --cc=security@kernel.org \
    --cc=tglx@linutronix.de \
    --cc=tiwai@suse.com \
    --cc=wang6495@umn.edu \
    --cc=yuehaibing@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).