Skip to contentSkip to conversation

USB hub code can dereference NULL hub and hub->ports

PATCH In review [email protected] 3 participants 8 messages Jan 20, 2025 – Feb 3, 2025 USB

lore
Share & tools
lore
#0 lore raw

The attached program, which acts via usbip as a USB device or hub, causes my linux machines to dereference some NULL pointers in drivers/usb/core/hub.c. These are places where udev->maxchild > 0, but either usb_hub_to_struct_hub(udev) returns NULL, or the returned hub has hub->ports == NULL.

This is one such place:

static void recursively_mark_NOTATTACHED(struct usb_device *udev)

{
        struct usb_hub *hub = usb_hub_to_struct_hub(udev);
        int i;

        for (i = 0; i < udev->maxchild; ++i) {
                if (hub->ports[i]->child)

And this:

static void hub_disconnect_children(struct usb_device *udev)

{
        struct usb_hub *hub = usb_hub_to_struct_hub(udev);
        int i;

        /* Free up all the children before we remove this device */
        for (i = 0; i < udev->maxchild; i++) {
                if (hub->ports[i]->child)

This can see NULL hub->ports:

void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
struct usb_hub_descriptor *desc)

{
        struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
        enum usb_port_connect_type connect_type;
        int i;

        if (!hub)
                return;

        if (!hub_is_superspeed(hdev)) {
                for (i = 1; i <= hdev->maxchild; i++) {
                        struct usb_port *port_dev = hub->ports[i - 1];

This can see a NULL hub:

static int hub_set_address(struct usb_device *udev, int devnum)

{
        int retval;
        unsigned int timeout_ms = USB_CTRL_SET_TIMEOUT;
        struct usb_hcd *hcd = bus_to_hcd(udev->bus);
        struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);

        if (hub->hdev->quirks & USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT)

I've attached a demo that runs into some of these NULL dereferences. It depends on being able to run usbip (and modeprobe vhci-hcd).

# uname -a
Linux xxx 6.13.0-rc3-00017-gf44d154d6e3d #14 SMP Mon Jan 20 04:52:59 EST 2025 x86_64 x86_64 x86_64 GNU/Linux
# cc usbhub11b.c
# ./a.out
...
hub 1-1:1.16: bad descriptor, ignoring hub
hub 1-1:1.16: probe with driver hub failed with error -5
BUG: kernel NULL pointer dereference, address: 0000000000000250
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
PGD 0 P4D 0
Oops: Oops: 0000 [#1] SMP DEBUG_PAGEALLOC PTI
CPU: 8 UID: 0 PID: 302 Comm: kworker/8:1 Not tainted 6.13.0-rc3-00017-gf44d154d6
e3d #14
Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
Workqueue: usb_hub_wq hub_event
RIP: 0010:recursively_mark_NOTATTACHED+0x37/0x90
Code: 48 85 ff 74 71 4c 8b a7 18 04 00 00 4d 85 e4 74 13 85 c0 74 3a 49 8b 94 24
 98 00 00 00 4c 8b a2 c8 00 00 00 85 c0 7e 27 31 db <49> 8b 84 24 50 02 00 00 48
 8b 04 d8 48 8b 38 48 85 ff 74 05 e8 b0
RSP: 0018:ffffc9000096bd28 EFLAGS: 00010046
RAX: 0000000000000001 RBX: 0000000000000000 RCX: ffff888106224c00
RDX: ffff8881037bc000 RSI: 0000000000000007 RDI: ffff88810a502000
RBP: ffff88810a502000 R08: 000000000000005a R09: 0000000000000000
R10: ffff88810a502000 R11: ffff88810a502000 R12: 0000000000000000
R13: ffff88810a502000 R14: ffff8881062241f0 R15: 0000000000000001
FS:  0000000000000000(0000) GS:ffff88842dc00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000250 CR3: 0000000003636001 CR4: 00000000003706f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 ? __die+0x1e/0x60
 ? page_fault_oops+0x157/0x450
 ? set_track_prepare+0x3b/0x60
 ? usb_control_msg+0xfd/0x150
 ? check_bytes_and_report.isra.0+0x48/0x120
 ? exc_page_fault+0x66/0x140
 ? asm_exc_page_fault+0x26/0x30
 ? recursively_mark_NOTATTACHED+0x37/0x90
 ? usb_control_msg+0xfd/0x150
 usb_disconnect+0x37/0x2c0
 hub_event+0xc8f/0x1870
 usb_disconnect+0x37/0x2c0
 hub_event+0xc8f/0x1870
 ? trace_event_raw_event_sched_switch+0x51/0x150
 process_one_work+0x13f/0x330
 worker_thread+0x25a/0x370
 ? _raw_spin_unlock_irqrestore+0xd/0x20
 ? __pfx_worker_thread+0x10/0x10
 kthread+0xdc/0x110
 ? __pfx_kthread+0x10/0x10
 ret_from_fork+0x2f/0x50
 ? __pfx_kthread+0x10/0x10
 ret_from_fork_asm+0x1a/0x30
 </TASK>

Robert Morris
[email protected]

maintainer
#1 lore raw
Quoted 54 lines · On Mon, Jan 20, 2025 at 12:27:19PM -0500, [email protected] wrote:

The attached program, which acts via usbip as a USB device or hub, causes my linux machines to dereference some NULL pointers in drivers/usb/core/hub.c. These are places where udev->maxchild > 0, but either usb_hub_to_struct_hub(udev) returns NULL, or the returned hub has hub->ports == NULL.

This is one such place:

static void recursively_mark_NOTATTACHED(struct usb_device *udev)

{
        struct usb_hub *hub = usb_hub_to_struct_hub(udev);
        int i;

        for (i = 0; i < udev->maxchild; ++i) {
                if (hub->ports[i]->child)

And this:

static void hub_disconnect_children(struct usb_device *udev)

{
        struct usb_hub *hub = usb_hub_to_struct_hub(udev);
        int i;

        /* Free up all the children before we remove this device */
        for (i = 0; i < udev->maxchild; i++) {
                if (hub->ports[i]->child)

This can see NULL hub->ports:

void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
struct usb_hub_descriptor *desc)

{
        struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
        enum usb_port_connect_type connect_type;
        int i;

        if (!hub)
                return;

        if (!hub_is_superspeed(hdev)) {
                for (i = 1; i <= hdev->maxchild; i++) {
                        struct usb_port *port_dev = hub->ports[i - 1];

This can see a NULL hub:

static int hub_set_address(struct usb_device *udev, int devnum)

{
        int retval;
        unsigned int timeout_ms = USB_CTRL_SET_TIMEOUT;
        struct usb_hcd *hcd = bus_to_hcd(udev->bus);
        struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);

        if (hub->hdev->quirks & USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT)
Quoted 2 lines

I've attached a demo that runs into some of these NULL dereferences. It depends on being able to run usbip (and modeprobe vhci-hcd).

Great, can you submit patches to fix these issues now that you have a reliable test program to verify the problem?

thanks,

greg k-h

#2 lore raw
Quoted 2 lines

Great, can you submit patches to fix these issues now that you have a reliable test program to verify the problem?

I think the problem is (at least sometimes) not that hub->ports is legitimately NULL and there's a missing check, but that usb_hub_to_struct_hub() returns an object of the wrong type so that hub->ports is junk, and only accidentally NULL in the demo I previously submitted.

I've attached a new demo which crashes because hub->ports is 0xcccccccccccccccc (on a kernel with red zones). The demo presents a USB device whose DeviceClass is a hub (9), with two interfaces, but the Vendor and Product indicate an FTDI serial adaptor.

First, usb_serial_probe() sets the interface zero dev->driver_data to a struct usb_serial.

Later, when the hub code is trying to set up interface one, it calls usb_hub_to_struct_hub(hdev):

struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)

{
        if (!hdev || !hdev->actconfig || !hdev->maxchild)
                return NULL;
        return usb_get_intfdata(hdev->actconfig->interface[0]);
}

interface[0], however, has been set up by the serial port code, and its dev->driver_data is a struct usb_serial, not a struct usb_hub. struct usb_serial is shorter than usb_hub, and as a result the end of the usb_hub is beyond the end of the allocated memory, which causes hub->ports to refer to bytes in the red zone.

I don't understand the code well enough to suggest a patch.

# cc usbser1c.c
# ./a.out
...
hub 1-1:1.0: bad descriptor, ignoring hub
hub 1-1:1.0: probe with driver hub failed with error -5
Oops: general protection fault, probably for non-canonical address 0xcccccccccccccccc: 0000 [#1] SMP DEBUG_PAGEALLOC PTI
CPU: 7 UID: 0 PID: 117 Comm: kworker/7:1 Not tainted 6.13.0-rc3-00017-gf44d154d6
e3d #14
Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
Workqueue: usb_hub_wq hub_event
RIP: 0010:usb_hub_adjust_deviceremovable+0x78/0x110
...
Call Trace:
 <TASK>
 ? die_addr+0x31/0x80
 ? exc_general_protection+0x1b4/0x3c0
 ? asm_exc_general_protection+0x26/0x30
 ? usb_hub_adjust_deviceremovable+0x78/0x110
 hub_probe+0x7c7/0xab0
 usb_probe_interface+0x14b/0x350
 really_probe+0xd0/0x2d0
 ? __pfx___device_attach_driver+0x10/0x10
 __driver_probe_device+0x6e/0x110
 driver_probe_device+0x1a/0x90
 __device_attach_driver+0x7e/0xc0
 bus_for_each_drv+0x7f/0xd0
 __device_attach+0xaa/0x1a0
 bus_probe_device+0x8b/0xa0
 device_add+0x62e/0x810
 usb_set_configuration+0x65d/0x990
 usb_generic_driver_probe+0x4b/0x70
 usb_probe_device+0x36/0xd0
 really_probe+0xd0/0x2d0
 ? __pfx___device_attach_driver+0x10/0x10
 __driver_probe_device+0x6e/0x110
 driver_probe_device+0x1a/0x90
 __device_attach_driver+0x7e/0xc0
 bus_for_each_drv+0x7f/0xd0
 __device_attach+0xaa/0x1a0
 bus_probe_device+0x8b/0xa0
 device_add+0x62e/0x810
 ? usb_detect_static_quirks+0x41/0xf0
 usb_new_device+0x1c8/0x400
 hub_event+0x1047/0x1870
 process_one_work+0x13f/0x330
 worker_thread+0x25a/0x370
 ? _raw_spin_unlock_irqrestore+0xd/0x20
 ? __pfx_worker_thread+0x10/0x10
 kthread+0xdc/0x110
 ? __pfx_kthread+0x10/0x10
 ret_from_fork+0x2f/0x50
 ? __pfx_kthread+0x10/0x10
 ret_from_fork_asm+0x1a/0x30

Robert Morris
[email protected]

#3 lore raw
Quoted 21 lines · On Wed, Jan 22, 2025 at 06:37:45AM -0500, [email protected] wrote:
Quoted 2 lines

Great, can you submit patches to fix these issues now that you have a reliable test program to verify the problem?

I think the problem is (at least sometimes) not that hub->ports is legitimately NULL and there's a missing check, but that usb_hub_to_struct_hub() returns an object of the wrong type so that hub->ports is junk, and only accidentally NULL in the demo I previously submitted.

I've attached a new demo which crashes because hub->ports is 0xcccccccccccccccc (on a kernel with red zones). The demo presents a USB device whose DeviceClass is a hub (9), with two interfaces, but the Vendor and Product indicate an FTDI serial adaptor.

First, usb_serial_probe() sets the interface zero dev->driver_data to a struct usb_serial.

Later, when the hub code is trying to set up interface one, it calls usb_hub_to_struct_hub(hdev):

struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)

Quoted 8 lines
{
        if (!hdev || !hdev->actconfig || !hdev->maxchild)
                return NULL;
        return usb_get_intfdata(hdev->actconfig->interface[0]);
}

interface[0], however, has been set up by the serial port code, and its dev->driver_data is a struct usb_serial, not a struct usb_hub.

Okay, that explains the problem. usb_hub_to_struct_hub() assumes that a hub device will never have more than one interface, because that requirement is in the USB spec. But of course, a bogus or malicious device can violate the requirement.

I think the best way to deal with this issue is to prevent the hub driver from binding to non-compliant devices. Does the patch below fix the problem for you?

Alan Stern

drivers/usb/core/hub.c +11 -0
Index: usb-devel/drivers/usb/core/hub.c
===================================================================
@@ -1848,6 +1848,17 @@ static int hub_probe(struct usb_interfac
 	hdev = interface_to_usbdev(intf);
 
 	/*
+	 * The USB 2.0 spec prohibits hubs from having more than one
+	 * configuration or interface, and we rely on this prohibition.
+	 * Refuse to accept a device that violates it.
+	 */
+	if (hdev->descriptor.bNumConfigurations > 1 ||
+			hdev->actconfig->desc.bNumInterfaces > 1) {
+		dev_err(&intf->dev, "Invalid hub with more than one config or interface\n");
+		return -EINVAL;
+	}
+
+	/*
 	 * Set default autosuspend delay as 0 to speedup bus suspend,
 	 * based on the below considerations:
 	 *
#4 lore raw

Alan,

Yes, your patch makes the NULL hub and hub->ports crashes I've seen go away!

Robert

Quoted 72 lines

Date: Wed, 22 Jan 2025 10:55:24 -0500
From: Alan Stern <[email protected]>
To: [email protected]
Cc: Greg KH <[email protected]>, [email protected]
Subject: Re: USB hub code can dereference NULL hub and hub->ports

Quoted 21 lines · On Wed, Jan 22, 2025 at 06:37:45AM -0500, [email protected] wrote:
Quoted 2 lines

Great, can you submit patches to fix these issues now that you have a reliable test program to verify the problem?

I think the problem is (at least sometimes) not that hub->ports is legitimately NULL and there's a missing check, but that usb_hub_to_struct_hub() returns an object of the wrong type so that hub->ports is junk, and only accidentally NULL in the demo I previously submitted.

I've attached a new demo which crashes because hub->ports is 0xcccccccccccccccc (on a kernel with red zones). The demo presents a USB device whose DeviceClass is a hub (9), with two interfaces, but the Vendor and Product indicate an FTDI serial adaptor.

First, usb_serial_probe() sets the interface zero dev->driver_data to a struct usb_serial.

Later, when the hub code is trying to set up interface one, it calls usb_hub_to_struct_hub(hdev):

struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)

Quoted 8 lines
{
        if (!hdev || !hdev->actconfig || !hdev->maxchild)
                return NULL;
        return usb_get_intfdata(hdev->actconfig->interface[0]);
}

interface[0], however, has been set up by the serial port code, and its dev->driver_data is a struct usb_serial, not a struct usb_hub.

Okay, that explains the problem. usb_hub_to_struct_hub() assumes that a hub device will never have more than one interface, because that requirement is in the USB spec. But of course, a bogus or malicious device can violate the requirement.

I think the best way to deal with this issue is to prevent the hub driver from binding to non-compliant devices. Does the patch below fix the problem for you?

Alan Stern

Index: usb-devel/drivers/usb/core/hub.c
===================================================================

drivers/usb/core/hub.c +11 -0
@@ -1848,6 +1848,17 @@ static int hub_probe(struct usb_interfac
 	hdev = interface_to_usbdev(intf);
 
 	/*
+	 * The USB 2.0 spec prohibits hubs from having more than one
+	 * configuration or interface, and we rely on this prohibition.
+	 * Refuse to accept a device that violates it.
+	 */
+	if (hdev->descriptor.bNumConfigurations > 1 ||
+			hdev->actconfig->desc.bNumInterfaces > 1) {
+		dev_err(&intf->dev, "Invalid hub with more than one config or interface\n");
+		return -EINVAL;
+	}
+
+	/*
 	 * Set default autosuspend delay as 0 to speedup bus suspend,
 	 * based on the below considerations:
 	 *
#5 lore raw

PATCH 1/1 USB: hub: Ignore non-compliant devices with too many configs or interfaces

Robert Morris created a test program which can cause usb_hub_to_struct_hub() to dereference a NULL or inappropriate pointer:

Oops: general protection fault, probably for non-canonical address
0xcccccccccccccccc: 0000 [#1] SMP DEBUG_PAGEALLOC PTI
CPU: 7 UID: 0 PID: 117 Comm: kworker/7:1 Not tainted 6.13.0-rc3-00017-gf44d154d6e3d #14
Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
Workqueue: usb_hub_wq hub_event
RIP: 0010:usb_hub_adjust_deviceremovable+0x78/0x110
...
Call Trace:
 <TASK>
 ? die_addr+0x31/0x80
 ? exc_general_protection+0x1b4/0x3c0
 ? asm_exc_general_protection+0x26/0x30
 ? usb_hub_adjust_deviceremovable+0x78/0x110
 hub_probe+0x7c7/0xab0
 usb_probe_interface+0x14b/0x350
 really_probe+0xd0/0x2d0
 ? __pfx___device_attach_driver+0x10/0x10
 __driver_probe_device+0x6e/0x110
 driver_probe_device+0x1a/0x90
 __device_attach_driver+0x7e/0xc0
 bus_for_each_drv+0x7f/0xd0
 __device_attach+0xaa/0x1a0
 bus_probe_device+0x8b/0xa0
 device_add+0x62e/0x810
 usb_set_configuration+0x65d/0x990
 usb_generic_driver_probe+0x4b/0x70
 usb_probe_device+0x36/0xd0

The cause of this error is that the device has two interfaces, and the hub driver binds to interface 1 instead of interface 0, which is where usb_hub_to_struct_hub() looks.

We can prevent the problem from occurring by refusing to accept hub devices that violate the USB spec by having more than one configuration or interface.

---

 drivers/usb/core/hub.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/usb/core/hub.c +11 -0
Index: usb-devel/drivers/usb/core/hub.c
===================================================================
@@ -1848,6 +1848,17 @@ static int hub_probe(struct usb_interfac
 	hdev = interface_to_usbdev(intf);
 
 	/*
+	 * The USB 2.0 spec prohibits hubs from having more than one
+	 * configuration or interface, and we rely on this prohibition.
+	 * Refuse to accept a device that violates it.
+	 */
+	if (hdev->descriptor.bNumConfigurations > 1 ||
+			hdev->actconfig->desc.bNumInterfaces > 1) {
+		dev_err(&intf->dev, "Invalid hub with more than one config or interface\n");
+		return -EINVAL;
+	}
+
+	/*
 	 * Set default autosuspend delay as 0 to speedup bus suspend,
 	 * based on the below considerations:
 	 *
#6 lore raw

Re: [PATCH] USB: hub: Ignore non-compliant devices with too many configs or interfaces

Quoted 40 lines · Alan Stern

Robert Morris created a test program which can cause usb_hub_to_struct_hub() to dereference a NULL or inappropriate pointer:

Oops: general protection fault, probably for non-canonical address
0xcccccccccccccccc: 0000 [#1] SMP DEBUG_PAGEALLOC PTI
CPU: 7 UID: 0 PID: 117 Comm: kworker/7:1 Not tainted 6.13.0-rc3-00017-gf44d154d6e3d #14
Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
Workqueue: usb_hub_wq hub_event
RIP: 0010:usb_hub_adjust_deviceremovable+0x78/0x110
...
Call Trace:
 <TASK>
 ? die_addr+0x31/0x80
 ? exc_general_protection+0x1b4/0x3c0
 ? asm_exc_general_protection+0x26/0x30
 ? usb_hub_adjust_deviceremovable+0x78/0x110
 hub_probe+0x7c7/0xab0
 usb_probe_interface+0x14b/0x350
 really_probe+0xd0/0x2d0
 ? __pfx___device_attach_driver+0x10/0x10
 __driver_probe_device+0x6e/0x110
 driver_probe_device+0x1a/0x90
 __device_attach_driver+0x7e/0xc0
 bus_for_each_drv+0x7f/0xd0
 __device_attach+0xaa/0x1a0
 bus_probe_device+0x8b/0xa0
 device_add+0x62e/0x810
 usb_set_configuration+0x65d/0x990
 usb_generic_driver_probe+0x4b/0x70
 usb_probe_device+0x36/0xd0

The cause of this error is that the device has two interfaces, and the hub driver binds to interface 1 instead of interface 0, which is where usb_hub_to_struct_hub() looks.

We can prevent the problem from occurring by refusing to accept hub devices that violate the USB spec by having more than one configuration or interface.

Quoted 5 lines

---

Greg:

I originally didn't mark this patch for the -stable kernels because it doesn't really fix a bug. But on the other hand, it does protect against a possible DOS attack from a malicious device, so perhaps it does belong in the -stable kernels.

I'll leave the decision up to you.

Alan Stern

Quoted 26 lines
 drivers/usb/core/hub.c |   11 +++++++++++
 1 file changed, 11 insertions(+)

Index: usb-devel/drivers/usb/core/hub.c
===================================================================

drivers/usb/core/hub.c +11 -0
@@ -1848,6 +1848,17 @@ static int hub_probe(struct usb_interfac
 	hdev = interface_to_usbdev(intf);
 
 	/*
+	 * The USB 2.0 spec prohibits hubs from having more than one
+	 * configuration or interface, and we rely on this prohibition.
+	 * Refuse to accept a device that violates it.
+	 */
+	if (hdev->descriptor.bNumConfigurations > 1 ||
+			hdev->actconfig->desc.bNumInterfaces > 1) {
+		dev_err(&intf->dev, "Invalid hub with more than one config or interface\n");
+		return -EINVAL;
+	}
+
+	/*
 	 * Set default autosuspend delay as 0 to speedup bus suspend,
 	 * based on the below considerations:
 	 *
maintainer
#7 lore raw

Re: [PATCH] USB: hub: Ignore non-compliant devices with too many configs or interfaces

Quoted 47 lines · Alan Stern
Quoted 40 lines · Alan Stern

Robert Morris created a test program which can cause usb_hub_to_struct_hub() to dereference a NULL or inappropriate pointer:

Oops: general protection fault, probably for non-canonical address
0xcccccccccccccccc: 0000 [#1] SMP DEBUG_PAGEALLOC PTI
CPU: 7 UID: 0 PID: 117 Comm: kworker/7:1 Not tainted 6.13.0-rc3-00017-gf44d154d6e3d #14
Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
Workqueue: usb_hub_wq hub_event
RIP: 0010:usb_hub_adjust_deviceremovable+0x78/0x110
...
Call Trace:
 <TASK>
 ? die_addr+0x31/0x80
 ? exc_general_protection+0x1b4/0x3c0
 ? asm_exc_general_protection+0x26/0x30
 ? usb_hub_adjust_deviceremovable+0x78/0x110
 hub_probe+0x7c7/0xab0
 usb_probe_interface+0x14b/0x350
 really_probe+0xd0/0x2d0
 ? __pfx___device_attach_driver+0x10/0x10
 __driver_probe_device+0x6e/0x110
 driver_probe_device+0x1a/0x90
 __device_attach_driver+0x7e/0xc0
 bus_for_each_drv+0x7f/0xd0
 __device_attach+0xaa/0x1a0
 bus_probe_device+0x8b/0xa0
 device_add+0x62e/0x810
 usb_set_configuration+0x65d/0x990
 usb_generic_driver_probe+0x4b/0x70
 usb_probe_device+0x36/0xd0

The cause of this error is that the device has two interfaces, and the hub driver binds to interface 1 instead of interface 0, which is where usb_hub_to_struct_hub() looks.

We can prevent the problem from occurring by refusing to accept hub devices that violate the USB spec by having more than one configuration or interface.

Quoted 5 lines

---

Quoted 8 lines

Greg:

I originally didn't mark this patch for the -stable kernels because it doesn't really fix a bug. But on the other hand, it does protect against a possible DOS attack from a malicious device, so perhaps it does belong in the -stable kernels.

I'll leave the decision up to you.

We need to protect from malicious USB devices as we have a whole history of that being an exploit vector for systems, with the most recent ones happening just a few months ago, allowing anyone full access to a locked Android device by just plugging in a device with some specially crafted USB configuration headers.

So let's try our best here where we can. Ideally the Android exploit shouldn't have even attempted to read the bad headers at the driver level, but this fix is at the hub level where I think all userspace "don't bind a driver unless we know it is ok" seem to ignore, so it's a good fix.

thanks,

greg k-h