ALSA: usb-audio: Check mixer unit descriptors more strictly
authorTakashi Iwai <tiwai@suse.de>
Wed, 19 Dec 2018 13:04:47 +0000 (14:04 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 7 Jan 2019 09:46:27 +0000 (10:46 +0100)
We've had some sanity checks of the mixer unit descriptors but they
are too loose and some corner cases are overlooked.  Add more strict
checks in uac_mixer_unit_get_channels() for avoiding possible OOB
accesses by malformed descriptors.

This also changes the semantics of uac_mixer_unit_get_channels()
slightly.  Now it returns zero for the cases where the descriptor
lacks of bmControls instead of -EINVAL.  Then the caller side skips
the mixer creation for such unit while it keeps parsing it.
This corresponds to the case like Maya44.

Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/mixer.c

index 0131de348cf64a214dea1ae4159b7b61b63deddb..dfd918891e692242c3857229ee188559294b033f 100644 (file)
@@ -753,8 +753,9 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
                                       struct uac_mixer_unit_descriptor *desc)
 {
        int mu_channels;
+       void *c;
 
-       if (desc->bLength < 11)
+       if (desc->bLength < sizeof(*desc))
                return -EINVAL;
        if (!desc->bNrInPins)
                return -EINVAL;
@@ -763,6 +764,8 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
        case UAC_VERSION_1:
        case UAC_VERSION_2:
        default:
+               if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 1)
+                       return 0; /* no bmControls -> skip */
                mu_channels = uac_mixer_unit_bNrChannels(desc);
                break;
        case UAC_VERSION_3:
@@ -772,7 +775,11 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
        }
 
        if (!mu_channels)
-               return -EINVAL;
+               return 0;
+
+       c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
+       if (c - (void *)desc + (mu_channels - 1) / 8 >= desc->bLength)
+               return 0; /* no bmControls -> skip */
 
        return mu_channels;
 }
@@ -944,7 +951,7 @@ static int check_input_term(struct mixer_build *state, int id,
                                struct uac_mixer_unit_descriptor *d = p1;
 
                                err = uac_mixer_unit_get_channels(state, d);
-                               if (err < 0)
+                               if (err <= 0)
                                        return err;
 
                                term->channels = err;
@@ -2118,7 +2125,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
                if (err < 0)
                        continue;
                /* no bmControls field (e.g. Maya44) -> ignore */
-               if (desc->bLength <= 10 + input_pins)
+               if (!num_outs)
                        continue;
                err = check_input_term(state, desc->baSourceID[pin], &iterm);
                if (err < 0)