diff options
author | Hui Peng <benquike@gmail.com> | 2019-08-15 00:31:34 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2019-08-15 15:48:52 -0400 |
commit | 19bce474c45be69a284ecee660aa12d8f1e88f18 (patch) | |
tree | 9a58decee9c325aa1dc879780b3bc778d6793e36 /sound/usb/mixer.c | |
parent | daac07156b330b18eb5071aec4b3ddca1c377f2c (diff) |
ALSA: usb-audio: Fix a stack buffer overflow bug in 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 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>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/mixer.c')
-rw-r--r-- | sound/usb/mixer.c | 35 |
1 files changed, 27 insertions, 8 deletions
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index ea487378be17..b5927c3d5bc0 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c | |||
@@ -68,6 +68,7 @@ struct mixer_build { | |||
68 | unsigned char *buffer; | 68 | unsigned char *buffer; |
69 | unsigned int buflen; | 69 | unsigned int buflen; |
70 | DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS); | 70 | DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS); |
71 | DECLARE_BITMAP(termbitmap, MAX_ID_ELEMS); | ||
71 | struct usb_audio_term oterm; | 72 | struct usb_audio_term oterm; |
72 | const struct usbmix_name_map *map; | 73 | const struct usbmix_name_map *map; |
73 | const struct usbmix_selector_map *selector_map; | 74 | const struct usbmix_selector_map *selector_map; |
@@ -775,16 +776,25 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, | |||
775 | * parse the source unit recursively until it reaches to a terminal | 776 | * parse the source unit recursively until it reaches to a terminal |
776 | * or a branched unit. | 777 | * or a branched unit. |
777 | */ | 778 | */ |
778 | static int check_input_term(struct mixer_build *state, int id, | 779 | static int __check_input_term(struct mixer_build *state, int id, |
779 | struct usb_audio_term *term) | 780 | struct usb_audio_term *term) |
780 | { | 781 | { |
781 | int protocol = state->mixer->protocol; | 782 | int protocol = state->mixer->protocol; |
782 | int err; | 783 | int err; |
783 | void *p1; | 784 | void *p1; |
785 | unsigned char *hdr; | ||
784 | 786 | ||
785 | memset(term, 0, sizeof(*term)); | 787 | memset(term, 0, sizeof(*term)); |
786 | while ((p1 = find_audio_control_unit(state, id)) != NULL) { | 788 | for (;;) { |
787 | unsigned char *hdr = p1; | 789 | /* a loop in the terminal chain? */ |
790 | if (test_and_set_bit(id, state->termbitmap)) | ||
791 | return -EINVAL; | ||
792 | |||
793 | p1 = find_audio_control_unit(state, id); | ||
794 | if (!p1) | ||
795 | break; | ||
796 | |||
797 | hdr = p1; | ||
788 | term->id = id; | 798 | term->id = id; |
789 | 799 | ||
790 | if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { | 800 | if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { |
@@ -802,7 +812,7 @@ static int check_input_term(struct mixer_build *state, int id, | |||
802 | 812 | ||
803 | /* call recursively to verify that the | 813 | /* call recursively to verify that the |
804 | * referenced clock entity is valid */ | 814 | * referenced clock entity is valid */ |
805 | err = check_input_term(state, d->bCSourceID, term); | 815 | err = __check_input_term(state, d->bCSourceID, term); |
806 | if (err < 0) | 816 | if (err < 0) |
807 | return err; | 817 | return err; |
808 | 818 | ||
@@ -836,7 +846,7 @@ static int check_input_term(struct mixer_build *state, int id, | |||
836 | case UAC2_CLOCK_SELECTOR: { | 846 | case UAC2_CLOCK_SELECTOR: { |
837 | struct uac_selector_unit_descriptor *d = p1; | 847 | struct uac_selector_unit_descriptor *d = p1; |
838 | /* call recursively to retrieve the channel info */ | 848 | /* call recursively to retrieve the channel info */ |
839 | err = check_input_term(state, d->baSourceID[0], term); | 849 | err = __check_input_term(state, d->baSourceID[0], term); |
840 | if (err < 0) | 850 | if (err < 0) |
841 | return err; | 851 | return err; |
842 | term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ | 852 | term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ |
@@ -899,7 +909,7 @@ static int check_input_term(struct mixer_build *state, int id, | |||
899 | 909 | ||
900 | /* call recursively to verify that the | 910 | /* call recursively to verify that the |
901 | * referenced clock entity is valid */ | 911 | * referenced clock entity is valid */ |
902 | err = check_input_term(state, d->bCSourceID, term); | 912 | err = __check_input_term(state, d->bCSourceID, term); |
903 | if (err < 0) | 913 | if (err < 0) |
904 | return err; | 914 | return err; |
905 | 915 | ||
@@ -950,7 +960,7 @@ static int check_input_term(struct mixer_build *state, int id, | |||
950 | case UAC3_CLOCK_SELECTOR: { | 960 | case UAC3_CLOCK_SELECTOR: { |
951 | struct uac_selector_unit_descriptor *d = p1; | 961 | struct uac_selector_unit_descriptor *d = p1; |
952 | /* call recursively to retrieve the channel info */ | 962 | /* call recursively to retrieve the channel info */ |
953 | err = check_input_term(state, d->baSourceID[0], term); | 963 | err = __check_input_term(state, d->baSourceID[0], term); |
954 | if (err < 0) | 964 | if (err < 0) |
955 | return err; | 965 | return err; |
956 | term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ | 966 | term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ |
@@ -966,7 +976,7 @@ static int check_input_term(struct mixer_build *state, int id, | |||
966 | return -EINVAL; | 976 | return -EINVAL; |
967 | 977 | ||
968 | /* call recursively to retrieve the channel info */ | 978 | /* call recursively to retrieve the channel info */ |
969 | err = check_input_term(state, d->baSourceID[0], term); | 979 | err = __check_input_term(state, d->baSourceID[0], term); |
970 | if (err < 0) | 980 | if (err < 0) |
971 | return err; | 981 | return err; |
972 | 982 | ||
@@ -984,6 +994,15 @@ static int check_input_term(struct mixer_build *state, int id, | |||
984 | return -ENODEV; | 994 | return -ENODEV; |
985 | } | 995 | } |
986 | 996 | ||
997 | |||
998 | static int check_input_term(struct mixer_build *state, int id, | ||
999 | struct usb_audio_term *term) | ||
1000 | { | ||
1001 | memset(term, 0, sizeof(*term)); | ||
1002 | memset(state->termbitmap, 0, sizeof(state->termbitmap)); | ||
1003 | return __check_input_term(state, id, term); | ||
1004 | } | ||
1005 | |||
987 | /* | 1006 | /* |
988 | * Feature Unit | 1007 | * Feature Unit |
989 | */ | 1008 | */ |