Skip to content

Commit

Permalink
ALSA: usb-audio: More validations of descriptor units
Browse files Browse the repository at this point in the history
Introduce a new helper to validate each audio descriptor unit before
and check the unit before actually accessing it.  This should harden
against the OOB access cases with malformed descriptors that have been
recently frequently reported by fuzzers.

The existing descriptor checks are still kept although they become
superfluous after this patch.  They'll be cleaned up eventually
later.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
tiwai committed Aug 22, 2019
1 parent f9f0e9e commit 57f8770
Show file tree
Hide file tree
Showing 7 changed files with 366 additions and 13 deletions.
3 changes: 2 additions & 1 deletion sound/usb/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ snd-usb-audio-objs := card.o \
power.o \
proc.o \
quirks.o \
stream.o
stream.o \
validate.o

snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o

Expand Down
4 changes: 4 additions & 0 deletions sound/usb/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber;
}

/* in validate.c */
bool snd_usb_validate_audio_desc(void *p, int protocol);
bool snd_usb_validate_midi_desc(void *p);

#endif /* __USBAUDIO_HELPER_H */
10 changes: 10 additions & 0 deletions sound/usb/mixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,8 @@ static int __check_input_term(struct mixer_build *state, int id,
p1 = find_audio_control_unit(state, id);
if (!p1)
break;
if (!snd_usb_validate_audio_desc(p1, protocol))
break; /* bad descriptor */

hdr = p1;
term->id = id;
Expand Down Expand Up @@ -2775,6 +2777,11 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
return -EINVAL;
}

if (!snd_usb_validate_audio_desc(p1, protocol)) {
usb_audio_dbg(state->chip, "invalid unit %d\n", unitid);
return 0; /* skip invalid unit */
}

if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
switch (p1[2]) {
case UAC_INPUT_TERMINAL:
Expand Down Expand Up @@ -3145,6 +3152,9 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
mixer->hostif->extralen,
p, UAC_OUTPUT_TERMINAL)) != NULL) {
if (!snd_usb_validate_audio_desc(p, mixer->protocol))
continue; /* skip invalid descriptor */

if (mixer->protocol == UAC_VERSION_1) {
struct uac1_output_terminal_descriptor *desc = p;

Expand Down
2 changes: 2 additions & 0 deletions sound/usb/power.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
struct uac3_power_domain_descriptor *pd_desc = p;
int i;

if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3))
continue;
for (i = 0; i < pd_desc->bNrEntities; i++) {
if (pd_desc->baEntityID[i] == id) {
pd->pd_id = pd_desc->bPowerDomainID;
Expand Down
3 changes: 3 additions & 0 deletions sound/usb/quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
NULL, USB_MS_MIDI_OUT_JACK);
if (!injd && !outjd)
return -ENODEV;
if (!snd_usb_validate_midi_desc(injd) ||
!snd_usb_validate_midi_desc(outjd))
return -ENODEV;
if (injd && (injd->bLength < 5 ||
(injd->bJackType != USB_MS_EMBEDDED &&
injd->bJackType != USB_MS_EXTERNAL)))
Expand Down
25 changes: 13 additions & 12 deletions sound/usb/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,16 +627,14 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
*/
static void *
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
int terminal_id, bool uac23)
int terminal_id, int protocol)
{
struct uac2_input_terminal_descriptor *term = NULL;
size_t minlen = uac23 ? sizeof(struct uac2_input_terminal_descriptor) :
sizeof(struct uac_input_terminal_descriptor);

while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_INPUT_TERMINAL))) {
if (term->bLength < minlen)
if (!snd_usb_validate_audio_desc(term, protocol))
continue;
if (term->bTerminalID == terminal_id)
return term;
Expand All @@ -647,16 +645,17 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,

static void *
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
int terminal_id)
int terminal_id, int protocol)
{
/* OK to use with both UAC2 and UAC3 */
struct uac2_output_terminal_descriptor *term = NULL;

while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_OUTPUT_TERMINAL))) {
if (term->bLength >= sizeof(*term) &&
term->bTerminalID == terminal_id)
if (!snd_usb_validate_audio_desc(term, protocol))
continue;
if (term->bTerminalID == terminal_id)
return term;
}

Expand Down Expand Up @@ -731,7 +730,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,

iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
false);
protocol);
if (iterm) {
num_channels = iterm->bNrChannels;
chconfig = le16_to_cpu(iterm->wChannelConfig);
Expand Down Expand Up @@ -767,7 +766,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
true);
protocol);
if (input_term) {
clock = input_term->bCSourceID;
if (!chconfig && (num_channels == input_term->bNrChannels))
Expand All @@ -776,7 +775,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
}

output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink);
as->bTerminalLink,
protocol);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
Expand Down Expand Up @@ -1002,14 +1002,15 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
true);
UAC_VERSION_3);
if (input_term) {
clock = input_term->bCSourceID;
goto found_clock;
}

output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink);
as->bTerminalLink,
UAC_VERSION_3);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
Expand Down
Loading

0 comments on commit 57f8770

Please sign in to comment.