[alsa-devel] [PATCH 0/4] Harden USB-audio parsers
Hi,
as recently spotted by syzkaller, the parser codes in USB audio driver don't check the length of the descriptor unit before actually accessing the extra field, which may lead to out-of-bound access error. This patchset tries to address these by adding proper sanity checks.
Takashi
===
Takashi Iwai (4): ALSA: usb-audio: Add sanity checks to FE parser ALSA: usb-audio: Fix potential out-of-bound access at parsing SU ALSA: usb-audio: Fix potential zero-division at parsing FU ALSA: usb-audio: Add sanity checks in v2 clock parsers
sound/usb/clock.c | 9 ++++++--- sound/usb/mixer.c | 19 ++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-)
When the usb-audio descriptor contains the malformed feature unit description with a too short length, the driver may access out-of-bounds. Add a sanity check of the header size at the beginning of parse_audio_feature_unit().
Fixes: 23caaf19b11e ("ALSA: usb-mixer: Add support for Audio Class v2.0") Reported-by: Andrey Konovalov andreyknvl@google.com Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 1beb5b4d027e..b8ce651e392c 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1469,6 +1469,12 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, __u8 *bmaControls;
if (state->mixer->protocol == UAC_VERSION_1) { + if (hdr->bLength < 7) { + usb_audio_err(state->chip, + "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", + unitid); + return -EINVAL; + } csize = hdr->bControlSize; if (!csize) { usb_audio_dbg(state->chip, @@ -1486,6 +1492,12 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, } } else { struct uac2_feature_unit_descriptor *ftr = _ftr; + if (hdr->bLength < 6) { + usb_audio_err(state->chip, + "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", + unitid); + return -EINVAL; + } csize = 4; channels = (hdr->bLength - 6) / 4 - 1; bmaControls = ftr->bmaControls;
The usb-audio driver may trigger an out-of-bound access at parsing a malformed selector unit, as it checks the header length only after evaluating bNrInPins field, which can be already above the given length. Fix it by adding the length check beforehand.
Fixes: 99fc86450c43 ("ALSA: usb-mixer: parse descriptors with structs") Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index b8ce651e392c..61b348383de8 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2098,7 +2098,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, const struct usbmix_name_map *map; char **namelist;
- if (!desc->bNrInPins || desc->bLength < 5 + desc->bNrInPins) { + if (desc->bLength < 5 || !desc->bNrInPins || + desc->bLength < 5 + desc->bNrInPins) { usb_audio_err(state->chip, "invalid SELECTOR UNIT descriptor %d\n", unitid); return -EINVAL;
parse_audio_feature_unit() contains a code dividing potentially with zero when a malformed FU descriptor is passed. Although there is already a sanity check, it checks only the value zero, hence it can still lead to a zero-division when a value 1 is passed there.
Fix it by correcting the sanity check (and the error message thereof).
Fixes: 23caaf19b11e ("ALSA: usb-mixer: Add support for Audio Class v2.0") Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 61b348383de8..0537c6322990 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1476,9 +1476,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, return -EINVAL; } csize = hdr->bControlSize; - if (!csize) { + if (csize <= 1) { usb_audio_dbg(state->chip, - "unit %u: invalid bControlSize == 0\n", + "unit %u: invalid bControlSize <= 1\n", unitid); return -EINVAL; }
The helper functions to parse and look for the clock source, selector and multiplier unit may return the descriptor with a too short length than required, while there is no sanity check in the caller side. Add some sanity checks in the parsers, at least, to guarantee the given descriptor size, for avoiding the potential crashes.
Fixes: 79f920fbff56 ("ALSA: usb-audio: parse clock topology of UAC2 devices") Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/clock.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 26dd5f20f149..eb3396ffba4c 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -43,7 +43,7 @@ static struct uac_clock_source_descriptor * while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_SOURCE))) { - if (cs->bClockID == clock_id) + if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id) return cs; }
@@ -59,8 +59,11 @@ static struct uac_clock_selector_descriptor * while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_SELECTOR))) { - if (cs->bClockID == clock_id) + if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id) { + if (cs->bLength < 5 + cs->bNrInPins) + return NULL; return cs; + } }
return NULL; @@ -75,7 +78,7 @@ static struct uac_clock_multiplier_descriptor * while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_MULTIPLIER))) { - if (cs->bClockID == clock_id) + if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id) return cs; }
participants (1)
-
Takashi Iwai