Re: WARNING in hso_free_net_device
On 9/5/2019 7:24 AM, Andrey Konovalov wrote: > On Thu, Sep 5, 2019 at 4:20 AM Hui Peng wrote: >> >> Can you guys have a look at the attached patch? > > Let's try it: > > #syz test: https://github.com/google/kasan.git eea39f24 > > FYI: there are two more reports coming from this driver, which might > (or might not) have the same root cause. One of them has a suggested > fix by Oliver. > > https://syzkaller.appspot.com/bug?extid=67b2bd0e34f952d0321e > https://syzkaller.appspot.com/bug?extid=93f2f45b19519b289613 > I think they are different, though similar. This one is resulted from unregistering a network device. These 2 are resulted from unregistering a tty device. >> >> On 9/4/19 6:41 PM, Stephen Hemminger wrote: >>> On Wed, 4 Sep 2019 16:27:50 -0400 >>> Hui Peng wrote: >>> >>>> Hi, all: >>>> >>>> I looked at the bug a little. >>>> >>>> The issue is that in the error handling code, hso_free_net_device >>>> unregisters >>>> >>>> the net_device (hso_net->net) by calling unregister_netdev. In the >>>> error handling code path, >>>> >>>> hso_net->net has not been registered yet. >>>> >>>> I think there are two ways to solve the issue: >>>> >>>> 1. fix it in drivers/net/usb/hso.c to avoiding unregistering the >>>> net_device when it is still not registered >>>> >>>> 2. fix it in unregister_netdev. We can add a field in net_device to >>>> record whether it is registered, and make unregister_netdev return if >>>> the net_device is not registered yet. >>>> >>>> What do you guys think ? >>> #1
Re: WARNING in hso_free_net_device
Can you guys have a look at the attached patch? On 9/4/19 6:41 PM, Stephen Hemminger wrote: > On Wed, 4 Sep 2019 16:27:50 -0400 > Hui Peng wrote: > >> Hi, all: >> >> I looked at the bug a little. >> >> The issue is that in the error handling code, hso_free_net_device >> unregisters >> >> the net_device (hso_net->net) by calling unregister_netdev. In the >> error handling code path, >> >> hso_net->net has not been registered yet. >> >> I think there are two ways to solve the issue: >> >> 1. fix it in drivers/net/usb/hso.c to avoiding unregistering the >> net_device when it is still not registered >> >> 2. fix it in unregister_netdev. We can add a field in net_device to >> record whether it is registered, and make unregister_netdev return if >> the net_device is not registered yet. >> >> What do you guys think ? > #1 From f3fdee8fc03aa2bc982f22da1d29bbf6bca72935 Mon Sep 17 00:00:00 2001 From: Hui Peng Date: Wed, 4 Sep 2019 21:38:35 -0400 Subject: [PATCH] Fix a wrong unregistering bug in hso_free_net_device As shown below, hso_create_net_device may call hso_free_net_device before the net_device is registered. hso_free_net_device will unregister the network device no matter it is registered or not, unregister_netdev is not able to handle unregistered net_device, resulting in the bug reported by the syzbot. ``` static struct hso_device *hso_create_net_device(struct usb_interface *interface, int port_spec) { .. net = alloc_netdev(sizeof(struct hso_net), "hso%d", NET_NAME_UNKNOWN, hso_net_init); .. if (!hso_net->out_endp) { dev_err(&interface->dev, "Can't find BULK OUT endpoint\n"); goto exit; } .. result = register_netdev(net); .. exit: hso_free_net_device(hso_dev); return NULL; } static void hso_free_net_device(struct hso_device *hso_dev) { .. if (hso_net->net) unregister_netdev(hso_net->net); .. } ``` This patch adds a net_registered field in struct hso_net to record whether the containing net_device is registered or not, and avoid unregistering it if it is not registered yet. Reported-by: syzbot+44d53c7255bb1aea2...@syzkaller.appspotmail.com Signed-off-by: Hui Peng --- drivers/net/usb/hso.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index ce78714..5b3df33 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -128,6 +128,7 @@ struct hso_shared_int { struct hso_net { struct hso_device *parent; struct net_device *net; + bool net_registered; struct rfkill *rfkill; char name[24]; @@ -2362,7 +2363,7 @@ static void hso_free_net_device(struct hso_device *hso_dev) remove_net_device(hso_net->parent); - if (hso_net->net) + if (hso_net->net && hso_net->net_registered) unregister_netdev(hso_net->net); /* start freeing */ @@ -2544,6 +2545,7 @@ static struct hso_device *hso_create_net_device(struct usb_interface *interface, dev_err(&interface->dev, "Failed to register device\n"); goto exit; } + hso_net->net_registered = true; hso_log_port(hso_dev); -- 2.7.4 pEpkey.asc Description: application/pgp-keys
Re: WARNING in hso_free_net_device
Hi, all: I looked at the bug a little. The issue is that in the error handling code, hso_free_net_device unregisters the net_device (hso_net->net) by calling unregister_netdev. In the error handling code path, hso_net->net has not been registered yet. I think there are two ways to solve the issue: 1. fix it in drivers/net/usb/hso.c to avoiding unregistering the net_device when it is still not registered 2. fix it in unregister_netdev. We can add a field in net_device to record whether it is registered, and make unregister_netdev return if the net_device is not registered yet. What do you guys think ? On 9/3/19 8:08 AM, syzbot wrote: > Hello, > > syzbot found the following crash on: > > HEAD commit: eea39f24 usb-fuzzer: main usb gadget fuzzer driver > git tree: https://github.com/google/kasan.git usb-fuzzer > console output: https://syzkaller.appspot.com/x/log.txt?x=15f17e6160 > kernel config: > https://syzkaller.appspot.com/x/.config?x=d0c62209eedfd54e > dashboard link: > https://syzkaller.appspot.com/bug?extid=44d53c7255bb1aea22d2 > compiler: gcc (GCC) 9.0.0 20181231 (experimental) > syz repro: > https://syzkaller.appspot.com/x/repro.syz?x=10ffdd1260 > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=15a738fe60 > > IMPORTANT: if you fix the bug, please add the following tag to the > commit: > Reported-by: syzbot+44d53c7255bb1aea2...@syzkaller.appspotmail.com > > usb 1-1: config 0 has no interface number 0 > usb 1-1: New USB device found, idVendor=0af0, idProduct=d257, > bcdDevice=4e.87 > usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0 > usb 1-1: config 0 descriptor?? > hso 1-1:0.15: Can't find BULK IN endpoint > [ cut here ] > WARNING: CPU: 1 PID: 83 at net/core/dev.c:8167 > rollback_registered_many.cold+0x41/0x1bc net/core/dev.c:8167 > Kernel panic - not syncing: panic_on_warn set ... > CPU: 1 PID: 83 Comm: kworker/1:2 Not tainted 5.3.0-rc5+ #28 > Hardware name: Google Google Compute Engine/Google Compute Engine, > BIOS Google 01/01/2011 > Workqueue: usb_hub_wq hub_event > Call Trace: > __dump_stack lib/dump_stack.c:77 [inline] > dump_stack+0xca/0x13e lib/dump_stack.c:113 > panic+0x2a3/0x6da kernel/panic.c:219 > __warn.cold+0x20/0x4a kernel/panic.c:576 > report_bug+0x262/0x2a0 lib/bug.c:186 > fixup_bug arch/x86/kernel/traps.c:179 [inline] > fixup_bug arch/x86/kernel/traps.c:174 [inline] > do_error_trap+0x12b/0x1e0 arch/x86/kernel/traps.c:272 > do_invalid_op+0x32/0x40 arch/x86/kernel/traps.c:291 > invalid_op+0x23/0x30 arch/x86/entry/entry_64.S:1028 > RIP: 0010:rollback_registered_many.cold+0x41/0x1bc net/core/dev.c:8167 > Code: ff e8 c7 26 90 fc 48 c7 c7 40 ec 63 86 e8 24 c8 7a fc 0f 0b e9 > 93 be ff ff e8 af 26 90 fc 48 c7 c7 40 ec 63 86 e8 0c c8 7a fc <0f> 0b > 4c 89 e7 e8 f9 12 34 fd 31 ff 41 89 c4 89 c6 e8 bd 27 90 fc > RSP: 0018:8881d934f088 EFLAGS: 00010282 > RAX: 0024 RBX: 8881d2ad4400 RCX: > RDX: RSI: 81288cfd RDI: ed103b269e03 > RBP: 8881d934f1b8 R08: 0024 R09: fbfff11ad794 > R10: fbfff11ad793 R11: 88d6bc9f R12: 8881d2ad4470 > R13: 8881d934f148 R14: dc00 R15: > rollback_registered+0xf2/0x1c0 net/core/dev.c:8243 > unregister_netdevice_queue net/core/dev.c:9290 [inline] > unregister_netdevice_queue+0x1d7/0x2b0 net/core/dev.c:9283 > unregister_netdevice include/linux/netdevice.h:2631 [inline] > unregister_netdev+0x18/0x20 net/core/dev.c:9331 > hso_free_net_device+0xff/0x300 drivers/net/usb/hso.c:2366 > hso_create_net_device+0x76d/0x9c0 drivers/net/usb/hso.c:2554 > hso_probe+0x28d/0x1a46 drivers/net/usb/hso.c:2931 > usb_probe_interface+0x305/0x7a0 drivers/usb/core/driver.c:361 > really_probe+0x281/0x6d0 drivers/base/dd.c:548 > driver_probe_device+0x101/0x1b0 drivers/base/dd.c:721 > __device_attach_driver+0x1c2/0x220 drivers/base/dd.c:828 > bus_for_each_drv+0x162/0x1e0 drivers/base/bus.c:454 > __device_attach+0x217/0x360 drivers/base/dd.c:894 > bus_probe_device+0x1e4/0x290 drivers/base/bus.c:514 > device_add+0xae6/0x16f0 drivers/base/core.c:2165 > usb_set_configuration+0xdf6/0x1670 drivers/usb/core/message.c:2023 > generic_probe+0x9d/0xd5 drivers/usb/core/generic.c:210 > usb_probe_device+0x99/0x100 drivers/usb/core/driver.c:266 > really_probe+0x281/0x6d0 drivers/base/dd.c:548 > driver_probe_device+0x101/0x1b0 drivers/base/dd.c:721 > __device_attach_driver+0x1c2/0x220 drivers/base/dd.c:828 > bus_for_each_drv+0x162/0x1e0 drivers/base/bus.c:454 > __device_attach+0x217/0x360 drivers/base/dd.c:894 > bus_probe_device+0x1e4/0x290 drivers/base/bus.c:514 > device_add+0xae6/0x16f0 drivers/base/core.c:2165 > usb_new_device.cold+0x6a4/0xe79 drivers/usb/core/hub.c:2536 > hub_port_connect drivers/usb/core/hub.c:5098 [inline] > hub_port_connect_change drivers/usb/core/hub.c:5213 [inline] > port_event drivers/usb/core/hub.c:5359 [inline] > hub_event+0x
Re: [PATCH 2/2] Fix a stack buffer overflow bug in check_input_term
On 9/1/19 9:00 AM, Salvatore Bonaccorso wrote: > Hi Hui, > > On Fri, Aug 30, 2019 at 05:47:29PM -0400, 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 and stop the execution if some id >> has been checked (similar to how parse_audio_unit handles unitid >> argument). >> >> CVE: CVE-2018-15118 > Similar to the previous one, this should be CVE-2019-15118 as far I > can tell. Same here: CVE id updated. Can you apply it to v4.4.190 and v4.14.141? Thanks. > > Regards, > Salvatore >From f5e478c4807b16f49c00316fb80485562b84340a Mon Sep 17 00:00:00 2001 From: Hui Peng Date: Fri, 30 Aug 2019 16:20:41 -0400 Subject: [PATCH 2/2] 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). CVE: CVE-2019-15118 Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- sound/usb/mixer.c | 29 - 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 10ddec76f906..e24572fd6e30 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -81,6 +81,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; @@ -709,15 +710,24 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm * 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, +static int __check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) { 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)) + return -EINVAL; + + p1 = find_audio_control_unit(state, id); + if (!p1) + break; + + hdr = p1; term->id = id; switch (hdr[2]) { case UAC_INPUT_TERMINAL: @@ -732,7 +742,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; @@ -764,7 +774,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 = d->bDescriptorSubtype << 16; /* virtual type */ @@ -811,6 +821,15 @@ 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.17.1
Re: [PATCH 1/2] Fix an OOB bug in parse_audio_mixer_unit
On 9/1/19 8:58 AM, Salvatore Bonaccorso wrote: > On Fri, Aug 30, 2019 at 05:46:49PM -0400, Hui Peng wrote: >> The `uac_mixer_unit_descriptor` shown as below is read from the >> device side. In `parse_audio_mixer_unit`, `baSourceID` field is >> accessed from index 0 to `bNrInPins` - 1, the current implementation >> assumes that descriptor is always valid (the length of descriptor >> is no shorter than 5 + `bNrInPins`). If a descriptor read from >> the device side is invalid, it may trigger out-of-bound memory >> access. >> >> ``` >> struct uac_mixer_unit_descriptor { >> __u8 bLength; >> __u8 bDescriptorType; >> __u8 bDescriptorSubtype; >> __u8 bUnitID; >> __u8 bNrInPins; >> __u8 baSourceID[]; >> } >> ``` >> >> This patch fixes the bug by add a sanity check on the length of >> the descriptor. >> >> CVE: CVE-2018-15117 > FWIW, the correct CVE id should be probably CVE-2019-15117 here. Yes, the CVE id was wrong. I have updated it in the attached patch. > But there was already a patch queued and released in 5.2.10 and > 4.19.68 for this issue (as far I can see; is this correct?) Yes, it should have been fixed in those branches. But google asked me to back port it to v4.4.190 and v4.14.141. I have mentioned it in one previous email, but it was blocked by vger because it was sent in html format. Can you apply it to these 2 versions? (it applies to both versions) Thanks. > Regards, > Salvatore >From 09942398a53bbe730264b782673890d4a54068d0 Mon Sep 17 00:00:00 2001 From: Hui Peng Date: Fri, 30 Aug 2019 16:11:00 -0400 Subject: [PATCH 1/2] Fix an OOB bug in parse_audio_mixer_unit The `uac_mixer_unit_descriptor` shown as below is read from the device side. In `parse_audio_mixer_unit`, `baSourceID` field is accessed from index 0 to `bNrInPins` - 1, the current implementation assumes that descriptor is always valid (the length of descriptor is no shorter than 5 + `bNrInPins`). If a descriptor read from the device side is invalid, it may trigger out-of-bound memory access. ``` struct uac_mixer_unit_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bUnitID; __u8 bNrInPins; __u8 baSourceID[]; } ``` This patch fixes the bug by add a sanity check on the length of the descriptor. CVE: CVE-2019-15117 Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- sound/usb/mixer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 1f7eb3816cd7..10ddec76f906 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1628,6 +1628,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, int pin, ich, err; if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || + desc->bLength < sizeof(*desc) + desc->bNrInPins || !(num_outs = uac_mixer_unit_bNrChannels(desc))) { usb_audio_err(state->chip, "invalid MIXER UNIT descriptor %d\n", -- 2.17.1
Re: [PATCH] Fix a double free bug in rsi_91x_deinit
On 8/31/19 2:18 PM, Guenter Roeck wrote: > On Mon, Aug 19, 2019 at 06:02:29PM -0400, Hui Peng wrote: >> `dev` (struct rsi_91x_usbdev *) field of adapter >> (struct rsi_91x_usbdev *) is allocated and initialized in >> `rsi_init_usb_interface`. If any error is detected in information >> read from the device side, `rsi_init_usb_interface` will be >> freed. However, in the higher level error handling code in >> `rsi_probe`, if error is detected, `rsi_91x_deinit` is called >> again, in which `dev` will be freed again, resulting double free. >> >> This patch fixes the double free by removing the free operation on >> `dev` in `rsi_init_usb_interface`, because `rsi_91x_deinit` is also >> used in `rsi_disconnect`, in that code path, the `dev` field is not >> (and thus needs to be) freed. >> >> This bug was found in v4.19, but is also present in the latest version >> of kernel. >> >> Reported-by: Hui Peng >> Reported-by: Mathias Payer >> Signed-off-by: Hui Peng > FWIW: > > Reviewed-by: Guenter Roeck > > This patch is listed as fix for CVE-2019-15504, which has a CVSS 2.0 score > of 10.0 (high) and CVSS 3.0 score of 9.8 (critical). > > Are there any plans to apply this patch to the upstream kernel anytime > soon ? If not, are there reasons to believe that the problem is not as > severe as its CVSS score may indicate ? This patch is also still under review. > Thanks, > Guenter > >> --- >> drivers/net/wireless/rsi/rsi_91x_usb.c | 1 - >> 1 file changed, 1 deletion(-) >> >> diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c >> b/drivers/net/wireless/rsi/rsi_91x_usb.c >> index c0a163e40402..ac917227f708 100644 >> --- a/drivers/net/wireless/rsi/rsi_91x_usb.c >> +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c >> @@ -640,7 +640,6 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter, >> kfree(rsi_dev->tx_buffer); >> >> fail_eps: >> -kfree(rsi_dev); >> >> return status; >> } >> -- >> 2.22.1 >>
[PATCH 1/2] Fix an OOB bug in parse_audio_mixer_unit
The `uac_mixer_unit_descriptor` shown as below is read from the device side. In `parse_audio_mixer_unit`, `baSourceID` field is accessed from index 0 to `bNrInPins` - 1, the current implementation assumes that descriptor is always valid (the length of descriptor is no shorter than 5 + `bNrInPins`). If a descriptor read from the device side is invalid, it may trigger out-of-bound memory access. ``` struct uac_mixer_unit_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bUnitID; __u8 bNrInPins; __u8 baSourceID[]; } ``` This patch fixes the bug by add a sanity check on the length of the descriptor. CVE: CVE-2018-15117 Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- sound/usb/mixer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 1f7eb3816cd7..10ddec76f906 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1628,6 +1628,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, int pin, ich, err; if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || + desc->bLength < sizeof(*desc) + desc->bNrInPins || !(num_outs = uac_mixer_unit_bNrChannels(desc))) { usb_audio_err(state->chip, "invalid MIXER UNIT descriptor %d\n", -- 2.17.1
[PATCH 2/2] 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). CVE: CVE-2018-15118 Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- sound/usb/mixer.c | 29 - 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 10ddec76f906..e24572fd6e30 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -81,6 +81,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; @@ -709,15 +710,24 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm * 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, +static int __check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) { 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)) + return -EINVAL; + + p1 = find_audio_control_unit(state, id); + if (!p1) + break; + + hdr = p1; term->id = id; switch (hdr[2]) { case UAC_INPUT_TERMINAL: @@ -732,7 +742,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; @@ -764,7 +774,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 = d->bDescriptorSubtype << 16; /* virtual type */ @@ -811,6 +821,15 @@ 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.17.1
Re: [PATCH] Fix an OOB access bug in technisat_usb2_get_ir
The following is the kasan report. This bug was found in v4.20-rc2, but it is present in the latest version. BUG: KASAN: slab-out-of-bounds in technisat_usb2_get_ir drivers/media/usb/dvb-usb/technisat-usb2.c:664 [inline] BUG: KASAN: slab-out-of-bounds in technisat_usb2_rc_query+0x598/0x5f0 drivers/media/usb/dvb-usb/technisat-usb2.c:679 Read of size 1 at addr 88805ee3d3a8 by task kworker/2:3/8681 CPU: 2 PID: 8681 Comm: kworker/2:3 Not tainted 4.20.0-rc2 #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014 Workqueue: events dvb_usb_read_remote_control Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xd2/0x148 lib/dump_stack.c:113 print_address_description+0x71/0x239 mm/kasan/report.c:256 kasan_report_error mm/kasan/report.c:354 [inline] kasan_report.cold.5+0x242/0x30b mm/kasan/report.c:412 __asan_report_load1_noabort+0x19/0x20 mm/kasan/report.c:430 technisat_usb2_get_ir drivers/media/usb/dvb-usb/technisat-usb2.c:664 [inline] technisat_usb2_rc_query+0x598/0x5f0 drivers/media/usb/dvb-usb/technisat-usb2.c:679 dvb_usb_read_remote_control+0xbd/0x150 drivers/media/usb/dvb-usb/dvb-usb-remote.c:261 process_one_work+0x816/0x14d0 kernel/workqueue.c:2153 worker_thread+0x9b/0xce0 kernel/workqueue.c:2296 kthread+0x33d/0x400 kernel/kthread.c:246 ret_from_fork+0x3a/0x50 arch/x86/entry/entry_64.S:352 On 8/20/19 2:19 PM, Hui Peng wrote: > In the while loop of technisat_usb2_get_ir, it scans through > a fix-sized buffer read from the device side, the termination > condition of the loop is `*b == 0xff`. If no `0xff` byte is read > from the device side, OOB access happens. > > This patch fixes the bug by adding an upper bound in the while loop. > > Reported-by: Hui Peng > Reported-by: Mathias Payer > Signed-off-by: Hui Peng > --- > drivers/media/usb/dvb-usb/technisat-usb2.c | 5 + > 1 file changed, 5 insertions(+) > > diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c > b/drivers/media/usb/dvb-usb/technisat-usb2.c > index c659e18b358b..181f5f97af45 100644 > --- a/drivers/media/usb/dvb-usb/technisat-usb2.c > +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c > @@ -612,6 +612,7 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d) > u8 *b; > int ret; > struct ir_raw_event ev; > + int i = 0; > > buf[0] = GET_IR_DATA_VENDOR_REQUEST; > buf[1] = 0x08; > @@ -656,11 +657,15 @@ static int technisat_usb2_get_ir(struct dvb_usb_device > *d) > > ev.pulse = 0; > while (1) { > + // only `ret` bytes are read from the device side > + if (i >= ret) > + break; > ev.pulse = !ev.pulse; > ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * > FIRMWARE_CLOCK_TICK) / 1000; > ir_raw_event_store(d->rc_dev, &ev); > > b++; > + i++; > if (*b == 0xff) { > ev.pulse = 0; > ev.duration = 88*2;
[PATCH] Fix an OOB access bug in technisat_usb2_get_ir
In the while loop of technisat_usb2_get_ir, it scans through a fix-sized buffer read from the device side, the termination condition of the loop is `*b == 0xff`. If no `0xff` byte is read from the device side, OOB access happens. This patch fixes the bug by adding an upper bound in the while loop. Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- drivers/media/usb/dvb-usb/technisat-usb2.c | 5 + 1 file changed, 5 insertions(+) diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c index c659e18b358b..181f5f97af45 100644 --- a/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -612,6 +612,7 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d) u8 *b; int ret; struct ir_raw_event ev; + int i = 0; buf[0] = GET_IR_DATA_VENDOR_REQUEST; buf[1] = 0x08; @@ -656,11 +657,15 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d) ev.pulse = 0; while (1) { + // only `ret` bytes are read from the device side + if (i >= ret) + break; ev.pulse = !ev.pulse; ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000; ir_raw_event_store(d->rc_dev, &ev); b++; + i++; if (*b == 0xff) { ev.pulse = 0; ev.duration = 88*2; -- 2.23.0
[PATCH] Fix a double free bug in rsi_91x_deinit
`dev` (struct rsi_91x_usbdev *) field of adapter (struct rsi_91x_usbdev *) is allocated and initialized in `rsi_init_usb_interface`. If any error is detected in information read from the device side, `rsi_init_usb_interface` will be freed. However, in the higher level error handling code in `rsi_probe`, if error is detected, `rsi_91x_deinit` is called again, in which `dev` will be freed again, resulting double free. This patch fixes the double free by removing the free operation on `dev` in `rsi_init_usb_interface`, because `rsi_91x_deinit` is also used in `rsi_disconnect`, in that code path, the `dev` field is not (and thus needs to be) freed. This bug was found in v4.19, but is also present in the latest version of kernel. Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- drivers/net/wireless/rsi/rsi_91x_usb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index c0a163e40402..ac917227f708 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -640,7 +640,6 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter, kfree(rsi_dev->tx_buffer); fail_eps: - kfree(rsi_dev); return status; } -- 2.22.1
[PATCH] Fix an OOB bug in uac_mixer_unit_bmControls
`uac_mixer_unit_get_channels` calls `uac_mixer_unit_bmControls` to get pointer to bmControls field. The current implementation of `uac_mixer_unit_get_channels` does properly check the size of uac_mixer_unit_descriptor descriptor and may allow OOB access in `uac_mixer_unit_bmControls`. Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- sound/usb/mixer.c | 25 ++--- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index b5927c3d5bc0..00e6274a63c3 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -738,28 +738,39 @@ static int get_cluster_channels_v3(struct mixer_build *state, unsigned int clust static int uac_mixer_unit_get_channels(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc) { - int mu_channels; + int mu_channels = 0; void *c; - if (desc->bLength < sizeof(*desc)) - return -EINVAL; if (!desc->bNrInPins) return -EINVAL; - if (desc->bLength < sizeof(*desc) + desc->bNrInPins) - return -EINVAL; switch (state->mixer->protocol) { case UAC_VERSION_1: + // limit derived from uac_mixer_unit_bmControls + if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 4) + return 0; + + mu_channels = uac_mixer_unit_bNrChannels(desc); + break; + case UAC_VERSION_2: - default: - if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 1) + // limit derived from uac_mixer_unit_bmControls + if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 6) return 0; /* no bmControls -> skip */ + mu_channels = uac_mixer_unit_bNrChannels(desc); break; case UAC_VERSION_3: + // limit derived from uac_mixer_unit_bmControls + if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 2) + return 0; /* no bmControls -> skip */ + mu_channels = get_cluster_channels_v3(state, uac3_mixer_unit_wClusterDescrID(desc)); break; + + default: + break; } if (!mu_channels) -- 2.22.1
[PATCH] Fix an OOB bug in uac_mixer_unit_bmControls
`uac_mixer_unit_get_channels` calls `uac_mixer_unit_bmControls` to get pointer to bmControls field. The current implementation of `uac_mixer_unit_get_channels` does properly check the size of uac_mixer_unit_descriptor descriptor and may allow OOB access in `uac_mixer_unit_bmControls`. Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- sound/usb/mixer.c | 25 ++--- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index b5927c3d5bc0..00e6274a63c3 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -738,28 +738,39 @@ static int get_cluster_channels_v3(struct mixer_build *state, unsigned int clust static int uac_mixer_unit_get_channels(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc) { - int mu_channels; + int mu_channels = 0; void *c; - if (desc->bLength < sizeof(*desc)) - return -EINVAL; if (!desc->bNrInPins) return -EINVAL; - if (desc->bLength < sizeof(*desc) + desc->bNrInPins) - return -EINVAL; switch (state->mixer->protocol) { case UAC_VERSION_1: + // limit derived from uac_mixer_unit_bmControls + if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 4) + return 0; + + mu_channels = uac_mixer_unit_bNrChannels(desc); + break; + case UAC_VERSION_2: - default: - if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 1) + // limit derived from uac_mixer_unit_bmControls + if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 6) return 0; /* no bmControls -> skip */ + mu_channels = uac_mixer_unit_bNrChannels(desc); break; case UAC_VERSION_3: + // limit derived from uac_mixer_unit_bmControls + if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 2) + return 0; /* no bmControls -> skip */ + mu_channels = get_cluster_channels_v3(state, uac3_mixer_unit_wClusterDescrID(desc)); break; + + default: + break; } if (!mu_channels) -- 2.22.1
Re: [PATCH] Fix a stack buffer overflow bug check_input_term
The stack trace differs from test to test, the attached trace1 file is taken from one of the tests. The bug is confirmed by adding some printk statement in `check_input_term`, the trace with output of printk is attached in trace2 file. This patch is a tentative fix to the bug, please give me feedback. On 8/15/19 12:35 AM, 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 > Reported-by: Mathias Payer > Signed-off-by: Hui Peng > --- > sound/usb/mixer.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index ea487378be17..1f6c8213df82 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; > @@ -782,6 +783,8 @@ static int check_input_term(struct mixer_build *state, > int id, > int err; > void *p1; > > + if (test_and_set_bit(id, state->termbitmap)) > + return 0; > memset(term, 0, sizeof(*term)); > while ((p1 = find_audio_control_unit(state, id)) != NULL) { > unsigned char *hdr = p1; [7.839002] usb 1-1: new high-speed USB device number 2 using xhci_hcd [7.966787] usb 1-1: Using ep0 maxpacket: 16 [7.969898] usb 1-1: string descriptor 0 read error: -22 [7.971507] usb 1-1: New USB device found, idVendor=046d, idProduct=0a44, bcdDevice= 1.27 [7.973874] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0 [7.979864] usb 1-1: current rate 16732531 is different from the runtime rate 48000 [7.982029] usb 1-1: current rate 11254477 is different from the runtime rate 48000 [7.987190] [parse_audio_unit:2797] reached [7.987741] [parse_audio_unit:2814] reached [7.988310] [parse_audio_feature_unit:1855] reached [7.988982] [parse_audio_feature_unit:1915] reached [7.989605] [parse_audio_unit:2797] reached [7.990173] [parse_audio_unit:2804] reached [7.999615] [parse_audio_unit:2797] reached [8.000166] [parse_audio_unit:2814] reached [8.000734] [parse_audio_feature_unit:1855] reached [8.001361] [parse_audio_feature_unit:1915] reached [8.002011] [parse_audio_unit:2797] reached [8.002590] [parse_audio_unit:2811] reached [8.003180] [parse_audio_unit:2797] reached [8.003754] [parse_audio_unit:2814] reached [8.004342] [parse_audio_feature_unit:1855] reached [8.004992] [parse_audio_feature_unit:1915] reached [8.005656] [parse_audio_feature_unit:1921] reached [8.006324] [check_input_term:804] reached [8.006881] [check_input_term:860] reached [8.007451] [check_input_term:804] reached [8.008038] [check_input_term:860] reached [8.008596] [check_input_term:804] reached [8.009129] [check_input_term:860] reached [8.009686] [check_input_term:804] reached [8.010217] [check_input_term:860] reached [8.010834] [check_input_term:804] reached [8.011430] [check_input_term:860] reached [8.011945] [check_input_term:804] reached [8.012577] [check_input_term:860] reached [8.013109] [check_input_term:804] reached [8.013704] [check_input_term:860] reached [8.014319] [check_input_term:804] reached [8.014848] [check_input_term:860] reached [8.015415] [check_input_term:804] reached [8.015964] [check_input_term:860] reached [8.016525] [check_input_term:804] reached [8.017055] [check_input_term:860] reached [8.017612] [check_input_term:804] reached [8.018168] [check_input_term:860] reached [8.018701] [check_input_term:804] reached [8.019273] [check_input_term:860] reached [8.019805] [check_input_term:804] reached [8.020362] [check_input_term:860] reached [8.020893] [check_input_term:804] reached [8.021440] [check_input_term:860] reached [8.021972] [check_input_term:804] reached [8.022536] [check_input_term:860] reached [8.023127] [check_input_term:804] reached [8.023664] [check_input_term:860] reached [8.024213] [check_input_term:804] reached [8.024791] [check_input_term:860] reached [8.025351]
[PATCH] 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). Reported-by: Hui Peng Reported-by: Mathias Payer Signed-off-by: Hui Peng --- sound/usb/mixer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index ea487378be17..1f6c8213df82 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; @@ -782,6 +783,8 @@ static int check_input_term(struct mixer_build *state, int id, int err; void *p1; + if (test_and_set_bit(id, state->termbitmap)) + return 0; memset(term, 0, sizeof(*term)); while ((p1 = find_audio_control_unit(state, id)) != NULL) { unsigned char *hdr = p1; -- 2.22.1
[PATCH] Fix an OOB bug in parse_audio_mixer_unit
The `uac_mixer_unit_descriptor` shown as below is read from the device side. In `parse_audio_mixer_unit`, `baSourceID` field is accessed from index 0 to `bNrInPins` - 1, the current implementation assumes that descriptor is always valid (the length of descriptor is no shorter than 5 + `bNrInPins`). If a descriptor read from the device side is invalid, it may trigger out-of-bound memory access. ``` struct uac_mixer_unit_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bUnitID; __u8 bNrInPins; __u8 baSourceID[]; } ``` This patch fixes the bug by add a sanity check on the length of the descriptor. Signed-off-by: Hui Peng Reported-by: Hui Peng Reported-by: Mathias Payer --- sound/usb/mixer.c | 9 + 1 file changed, 9 insertions(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 7498b5191b68..38202ce67237 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2091,6 +2091,15 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, struct usb_audio_term iterm; int input_pins, num_ins, num_outs; int pin, ich, err; + int desc_len = (int) ((unsigned long) state->buffer + + state->buflen - (unsigned long) raw_desc); + + if (desc_len < sizeof(*desc) + desc->bNrInPins) { + usb_audio_err(state->chip, + "descriptor %d too short\n", + unitid); + return -EINVAL; + } err = uac_mixer_unit_get_channels(state, desc); if (err < 0) { -- 2.22.1
[PATCH 1/2] Fix a NULL-ptr-deref bug in ath6kl_usb_alloc_urb_from_pipe
The `ar_usb` field of `ath6kl_usb_pipe_usb_pipe` objects are initialized to point to the containing `ath6kl_usb` object according to endpoint descriptors read from the device side, as shown below in `ath6kl_usb_setup_pipe_resources`: for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; // get the address from endpoint descriptor pipe_num = ath6kl_usb_get_logical_pipe_num(ar_usb, endpoint->bEndpointAddress, &urbcount); .. // select the pipe object pipe = &ar_usb->pipes[pipe_num]; // initialize the ar_usb field pipe->ar_usb = ar_usb; } The driver assumes that the addresses reported in endpoint descriptors from device side to be complete. If a device is malicious and does not report complete addresses, it may trigger NULL-ptr-deref `ath6kl_usb_alloc_urb_from_pipe` and `ath6kl_usb_free_urb_to_pipe`. This patch fixes the bug by preventing potential NULL-ptr-deref. Signed-off-by: Hui Peng Reported-by: Hui Peng Reported-by: Mathias Payer --- drivers/net/wireless/ath/ath6kl/usb.c | 8 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 4defb7a0330f..53b66e9434c9 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -132,6 +132,10 @@ ath6kl_usb_alloc_urb_from_pipe(struct ath6kl_usb_pipe *pipe) struct ath6kl_urb_context *urb_context = NULL; unsigned long flags; + /* bail if this pipe is not initialized */ + if (!pipe->ar_usb) + return NULL; + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); if (!list_empty(&pipe->urb_list_head)) { urb_context = @@ -150,6 +154,10 @@ static void ath6kl_usb_free_urb_to_pipe(struct ath6kl_usb_pipe *pipe, { unsigned long flags; + /* bail if this pipe is not initialized */ + if (!pipe->ar_usb) + return; + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); pipe->urb_cnt++; -- 2.22.0