]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Jan 2022 10:52:30 +0000 (11:52 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Jan 2022 10:52:30 +0000 (11:52 +0100)
added patches:
bluetooth-bfusb-fix-division-by-zero-in-send-path.patch
can-bcm-switch-timer-to-hrtimer_mode_soft-and-remove-hrtimer_tasklet.patch
usb-core-fix-bug-in-resuming-hub-s-handling-of-wakeup-requests.patch
usb-fix-slab-out-of-bounds-write-bug-in-usb_hcd_poll_rh_status.patch

queue-4.19/bluetooth-bfusb-fix-division-by-zero-in-send-path.patch [new file with mode: 0644]
queue-4.19/can-bcm-switch-timer-to-hrtimer_mode_soft-and-remove-hrtimer_tasklet.patch [new file with mode: 0644]
queue-4.19/series
queue-4.19/usb-core-fix-bug-in-resuming-hub-s-handling-of-wakeup-requests.patch [new file with mode: 0644]
queue-4.19/usb-fix-slab-out-of-bounds-write-bug-in-usb_hcd_poll_rh_status.patch [new file with mode: 0644]

diff --git a/queue-4.19/bluetooth-bfusb-fix-division-by-zero-in-send-path.patch b/queue-4.19/bluetooth-bfusb-fix-division-by-zero-in-send-path.patch
new file mode 100644 (file)
index 0000000..201f71e
--- /dev/null
@@ -0,0 +1,38 @@
+From b5e6fa7a12572c82f1e7f2f51fbb02a322291291 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Mon, 25 Oct 2021 13:39:44 +0200
+Subject: Bluetooth: bfusb: fix division by zero in send path
+
+From: Johan Hovold <johan@kernel.org>
+
+commit b5e6fa7a12572c82f1e7f2f51fbb02a322291291 upstream.
+
+Add the missing bulk-out endpoint sanity check to probe() to avoid
+division by zero in bfusb_send_frame() in case a malicious device has
+broken descriptors (or when doing descriptor fuzz testing).
+
+Note that USB core will reject URBs submitted for endpoints with zero
+wMaxPacketSize but that drivers doing packet-size calculations still
+need to handle this (cf. commit 2548288b4fb0 ("USB: Fix: Don't skip
+endpoint descriptors with maxpacket=0")).
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/bluetooth/bfusb.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/bluetooth/bfusb.c
++++ b/drivers/bluetooth/bfusb.c
+@@ -644,6 +644,9 @@ static int bfusb_probe(struct usb_interf
+       data->bulk_out_ep   = bulk_out_ep->desc.bEndpointAddress;
+       data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
++      if (!data->bulk_pkt_size)
++              goto done;
++
+       rwlock_init(&data->lock);
+       data->reassembly = NULL;
diff --git a/queue-4.19/can-bcm-switch-timer-to-hrtimer_mode_soft-and-remove-hrtimer_tasklet.patch b/queue-4.19/can-bcm-switch-timer-to-hrtimer_mode_soft-and-remove-hrtimer_tasklet.patch
new file mode 100644 (file)
index 0000000..7462846
--- /dev/null
@@ -0,0 +1,318 @@
+From bf74aa86e111aa3b2fbb25db37e3a3fab71b5b68 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Mon, 12 Aug 2019 14:57:14 +0200
+Subject: can: bcm: switch timer to HRTIMER_MODE_SOFT and remove hrtimer_tasklet
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit bf74aa86e111aa3b2fbb25db37e3a3fab71b5b68 upstream.
+
+This patch switches the timer to HRTIMER_MODE_SOFT, which executed the
+timer callback in softirq context and removes the hrtimer_tasklet.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
+Acked-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Ziyang Xuan <william.xuanziyang@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/can/bcm.c |  156 +++++++++++++++++++---------------------------------------
+ 1 file changed, 52 insertions(+), 104 deletions(-)
+
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -105,7 +105,6 @@ struct bcm_op {
+       unsigned long frames_abs, frames_filtered;
+       struct bcm_timeval ival1, ival2;
+       struct hrtimer timer, thrtimer;
+-      struct tasklet_struct tsklet, thrtsklet;
+       ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
+       int rx_ifindex;
+       int cfsiz;
+@@ -374,25 +373,34 @@ static void bcm_send_to_user(struct bcm_
+       }
+ }
+-static void bcm_tx_start_timer(struct bcm_op *op)
++static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
+ {
++      ktime_t ival;
++
+       if (op->kt_ival1 && op->count)
+-              hrtimer_start(&op->timer,
+-                            ktime_add(ktime_get(), op->kt_ival1),
+-                            HRTIMER_MODE_ABS);
++              ival = op->kt_ival1;
+       else if (op->kt_ival2)
+-              hrtimer_start(&op->timer,
+-                            ktime_add(ktime_get(), op->kt_ival2),
+-                            HRTIMER_MODE_ABS);
++              ival = op->kt_ival2;
++      else
++              return false;
++
++      hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
++      return true;
+ }
+-static void bcm_tx_timeout_tsklet(unsigned long data)
++static void bcm_tx_start_timer(struct bcm_op *op)
+ {
+-      struct bcm_op *op = (struct bcm_op *)data;
++      if (bcm_tx_set_expiry(op, &op->timer))
++              hrtimer_start_expires(&op->timer, HRTIMER_MODE_ABS_SOFT);
++}
++
++/* bcm_tx_timeout_handler - performs cyclic CAN frame transmissions */
++static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
++{
++      struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+       struct bcm_msg_head msg_head;
+       if (op->kt_ival1 && (op->count > 0)) {
+-
+               op->count--;
+               if (!op->count && (op->flags & TX_COUNTEVT)) {
+@@ -410,22 +418,12 @@ static void bcm_tx_timeout_tsklet(unsign
+               }
+               bcm_can_tx(op);
+-      } else if (op->kt_ival2)
++      } else if (op->kt_ival2) {
+               bcm_can_tx(op);
++      }
+-      bcm_tx_start_timer(op);
+-}
+-
+-/*
+- * bcm_tx_timeout_handler - performs cyclic CAN frame transmissions
+- */
+-static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+-{
+-      struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+-
+-      tasklet_schedule(&op->tsklet);
+-
+-      return HRTIMER_NORESTART;
++      return bcm_tx_set_expiry(op, &op->timer) ?
++              HRTIMER_RESTART : HRTIMER_NORESTART;
+ }
+ /*
+@@ -492,7 +490,7 @@ static void bcm_rx_update_and_send(struc
+               /* do not send the saved data - only start throttle timer */
+               hrtimer_start(&op->thrtimer,
+                             ktime_add(op->kt_lastmsg, op->kt_ival2),
+-                            HRTIMER_MODE_ABS);
++                            HRTIMER_MODE_ABS_SOFT);
+               return;
+       }
+@@ -551,14 +549,21 @@ static void bcm_rx_starttimer(struct bcm
+               return;
+       if (op->kt_ival1)
+-              hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL);
++              hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL_SOFT);
+ }
+-static void bcm_rx_timeout_tsklet(unsigned long data)
++/* bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out */
++static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+ {
+-      struct bcm_op *op = (struct bcm_op *)data;
++      struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+       struct bcm_msg_head msg_head;
++      /* if user wants to be informed, when cyclic CAN-Messages come back */
++      if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
++              /* clear received CAN frames to indicate 'nothing received' */
++              memset(op->last_frames, 0, op->nframes * op->cfsiz);
++      }
++
+       /* create notification to user */
+       memset(&msg_head, 0, sizeof(msg_head));
+       msg_head.opcode  = RX_TIMEOUT;
+@@ -570,25 +575,6 @@ static void bcm_rx_timeout_tsklet(unsign
+       msg_head.nframes = 0;
+       bcm_send_to_user(op, &msg_head, NULL, 0);
+-}
+-
+-/*
+- * bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out
+- */
+-static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+-{
+-      struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+-
+-      /* schedule before NET_RX_SOFTIRQ */
+-      tasklet_hi_schedule(&op->tsklet);
+-
+-      /* no restart of the timer is done here! */
+-
+-      /* if user wants to be informed, when cyclic CAN-Messages come back */
+-      if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
+-              /* clear received CAN frames to indicate 'nothing received' */
+-              memset(op->last_frames, 0, op->nframes * op->cfsiz);
+-      }
+       return HRTIMER_NORESTART;
+ }
+@@ -596,14 +582,12 @@ static enum hrtimer_restart bcm_rx_timeo
+ /*
+  * bcm_rx_do_flush - helper for bcm_rx_thr_flush
+  */
+-static inline int bcm_rx_do_flush(struct bcm_op *op, int update,
+-                                unsigned int index)
++static inline int bcm_rx_do_flush(struct bcm_op *op, unsigned int index)
+ {
+       struct canfd_frame *lcf = op->last_frames + op->cfsiz * index;
+       if ((op->last_frames) && (lcf->flags & RX_THR)) {
+-              if (update)
+-                      bcm_rx_changed(op, lcf);
++              bcm_rx_changed(op, lcf);
+               return 1;
+       }
+       return 0;
+@@ -611,11 +595,8 @@ static inline int bcm_rx_do_flush(struct
+ /*
+  * bcm_rx_thr_flush - Check for throttled data and send it to the userspace
+- *
+- * update == 0 : just check if throttled data is available  (any irq context)
+- * update == 1 : check and send throttled data to userspace (soft_irq context)
+  */
+-static int bcm_rx_thr_flush(struct bcm_op *op, int update)
++static int bcm_rx_thr_flush(struct bcm_op *op)
+ {
+       int updated = 0;
+@@ -624,24 +605,16 @@ static int bcm_rx_thr_flush(struct bcm_o
+               /* for MUX filter we start at index 1 */
+               for (i = 1; i < op->nframes; i++)
+-                      updated += bcm_rx_do_flush(op, update, i);
++                      updated += bcm_rx_do_flush(op, i);
+       } else {
+               /* for RX_FILTER_ID and simple filter */
+-              updated += bcm_rx_do_flush(op, update, 0);
++              updated += bcm_rx_do_flush(op, 0);
+       }
+       return updated;
+ }
+-static void bcm_rx_thr_tsklet(unsigned long data)
+-{
+-      struct bcm_op *op = (struct bcm_op *)data;
+-
+-      /* push the changed data to the userspace */
+-      bcm_rx_thr_flush(op, 1);
+-}
+-
+ /*
+  * bcm_rx_thr_handler - the time for blocked content updates is over now:
+  *                      Check for throttled data and send it to the userspace
+@@ -650,9 +623,7 @@ static enum hrtimer_restart bcm_rx_thr_h
+ {
+       struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
+-      tasklet_schedule(&op->thrtsklet);
+-
+-      if (bcm_rx_thr_flush(op, 0)) {
++      if (bcm_rx_thr_flush(op)) {
+               hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
+               return HRTIMER_RESTART;
+       } else {
+@@ -748,23 +719,8 @@ static struct bcm_op *bcm_find_op(struct
+ static void bcm_remove_op(struct bcm_op *op)
+ {
+-      if (op->tsklet.func) {
+-              while (test_bit(TASKLET_STATE_SCHED, &op->tsklet.state) ||
+-                     test_bit(TASKLET_STATE_RUN, &op->tsklet.state) ||
+-                     hrtimer_active(&op->timer)) {
+-                      hrtimer_cancel(&op->timer);
+-                      tasklet_kill(&op->tsklet);
+-              }
+-      }
+-
+-      if (op->thrtsklet.func) {
+-              while (test_bit(TASKLET_STATE_SCHED, &op->thrtsklet.state) ||
+-                     test_bit(TASKLET_STATE_RUN, &op->thrtsklet.state) ||
+-                     hrtimer_active(&op->thrtimer)) {
+-                      hrtimer_cancel(&op->thrtimer);
+-                      tasklet_kill(&op->thrtsklet);
+-              }
+-      }
++      hrtimer_cancel(&op->timer);
++      hrtimer_cancel(&op->thrtimer);
+       if ((op->frames) && (op->frames != &op->sframe))
+               kfree(op->frames);
+@@ -998,15 +954,13 @@ static int bcm_tx_setup(struct bcm_msg_h
+               op->ifindex = ifindex;
+               /* initialize uninitialized (kzalloc) structure */
+-              hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++              hrtimer_init(&op->timer, CLOCK_MONOTONIC,
++                           HRTIMER_MODE_REL_SOFT);
+               op->timer.function = bcm_tx_timeout_handler;
+-              /* initialize tasklet for tx countevent notification */
+-              tasklet_init(&op->tsklet, bcm_tx_timeout_tsklet,
+-                           (unsigned long) op);
+-
+               /* currently unused in tx_ops */
+-              hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++              hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC,
++                           HRTIMER_MODE_REL_SOFT);
+               /* add this bcm_op to the list of the tx_ops */
+               list_add(&op->list, &bo->tx_ops);
+@@ -1175,20 +1129,14 @@ static int bcm_rx_setup(struct bcm_msg_h
+               op->rx_ifindex = ifindex;
+               /* initialize uninitialized (kzalloc) structure */
+-              hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++              hrtimer_init(&op->timer, CLOCK_MONOTONIC,
++                           HRTIMER_MODE_REL_SOFT);
+               op->timer.function = bcm_rx_timeout_handler;
+-              /* initialize tasklet for rx timeout notification */
+-              tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet,
+-                           (unsigned long) op);
+-
+-              hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++              hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC,
++                           HRTIMER_MODE_REL_SOFT);
+               op->thrtimer.function = bcm_rx_thr_handler;
+-              /* initialize tasklet for rx throttle handling */
+-              tasklet_init(&op->thrtsklet, bcm_rx_thr_tsklet,
+-                           (unsigned long) op);
+-
+               /* add this bcm_op to the list of the rx_ops */
+               list_add(&op->list, &bo->rx_ops);
+@@ -1234,12 +1182,12 @@ static int bcm_rx_setup(struct bcm_msg_h
+                        */
+                       op->kt_lastmsg = 0;
+                       hrtimer_cancel(&op->thrtimer);
+-                      bcm_rx_thr_flush(op, 1);
++                      bcm_rx_thr_flush(op);
+               }
+               if ((op->flags & STARTTIMER) && op->kt_ival1)
+                       hrtimer_start(&op->timer, op->kt_ival1,
+-                                    HRTIMER_MODE_REL);
++                                    HRTIMER_MODE_REL_SOFT);
+       }
+       /* now we can register for can_ids, if we added a new bcm_op */
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..71e683acc0e3f7422f9a0aacd388b32e04980802 100644 (file)
@@ -0,0 +1,4 @@
+bluetooth-bfusb-fix-division-by-zero-in-send-path.patch
+usb-core-fix-bug-in-resuming-hub-s-handling-of-wakeup-requests.patch
+usb-fix-slab-out-of-bounds-write-bug-in-usb_hcd_poll_rh_status.patch
+can-bcm-switch-timer-to-hrtimer_mode_soft-and-remove-hrtimer_tasklet.patch
diff --git a/queue-4.19/usb-core-fix-bug-in-resuming-hub-s-handling-of-wakeup-requests.patch b/queue-4.19/usb-core-fix-bug-in-resuming-hub-s-handling-of-wakeup-requests.patch
new file mode 100644 (file)
index 0000000..bb90805
--- /dev/null
@@ -0,0 +1,69 @@
+From 0f663729bb4afc92a9986b66131ebd5b8a9254d1 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Sat, 1 Jan 2022 14:52:14 -0500
+Subject: USB: core: Fix bug in resuming hub's handling of wakeup requests
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 0f663729bb4afc92a9986b66131ebd5b8a9254d1 upstream.
+
+Bugzilla #213839 reports a 7-port hub that doesn't work properly when
+devices are plugged into some of the ports; the kernel goes into an
+unending disconnect/reinitialize loop as shown in the bug report.
+
+This "7-port hub" comprises two four-port hubs with one plugged into
+the other; the failures occur when a device is plugged into one of the
+downstream hub's ports.  (These hubs have other problems too.  For
+example, they bill themselves as USB-2.0 compliant but they only run
+at full speed.)
+
+It turns out that the failures are caused by bugs in both the kernel
+and the hub.  The hub's bug is that it reports a different
+bmAttributes value in its configuration descriptor following a remote
+wakeup (0xe0 before, 0xc0 after -- the wakeup-support bit has
+changed).
+
+The kernel's bug is inside the hub driver's resume handler.  When
+hub_activate() sees that one of the hub's downstream ports got a
+wakeup request from a child device, it notes this fact by setting the
+corresponding bit in the hub->change_bits variable.  But this variable
+is meant for connection changes, not wakeup events; setting it causes
+the driver to believe the downstream port has been disconnected and
+then connected again (in addition to having received a wakeup
+request).
+
+Because of this, the hub driver then tries to check whether the device
+currently plugged into the downstream port is the same as the device
+that had been attached there before.  Normally this check succeeds and
+wakeup handling continues with no harm done (which is why the bug
+remained undetected until now).  But with these dodgy hubs, the check
+fails because the config descriptor has changed.  This causes the hub
+driver to reinitialize the child device, leading to the
+disconnect/reinitialize loop described in the bug report.
+
+The proper way to note reception of a downstream wakeup request is
+to set a bit in the hub->event_bits variable instead of
+hub->change_bits.  That way the hub driver will realize that something
+has happened to the port but will not think the port and child device
+have been disconnected.  This patch makes that change.
+
+Cc: <stable@vger.kernel.org>
+Tested-by: Jonathan McDowell <noodles@earth.li>
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Link: https://lore.kernel.org/r/YdCw7nSfWYPKWQoD@rowland.harvard.edu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -1197,7 +1197,7 @@ static void hub_activate(struct usb_hub
+                        */
+                       if (portchange || (hub_is_superspeed(hub->hdev) &&
+                                               port_resumed))
+-                              set_bit(port1, hub->change_bits);
++                              set_bit(port1, hub->event_bits);
+               } else if (udev->persist_enabled) {
+ #ifdef CONFIG_PM
diff --git a/queue-4.19/usb-fix-slab-out-of-bounds-write-bug-in-usb_hcd_poll_rh_status.patch b/queue-4.19/usb-fix-slab-out-of-bounds-write-bug-in-usb_hcd_poll_rh_status.patch
new file mode 100644 (file)
index 0000000..68a42e7
--- /dev/null
@@ -0,0 +1,65 @@
+From 1d7d4c07932e04355d6e6528d44a2f2c9e354346 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 31 Dec 2021 21:07:12 -0500
+Subject: USB: Fix "slab-out-of-bounds Write" bug in usb_hcd_poll_rh_status
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 1d7d4c07932e04355d6e6528d44a2f2c9e354346 upstream.
+
+When the USB core code for getting root-hub status reports was
+originally written, it was assumed that the hub driver would be its
+only caller.  But this isn't true now; user programs can use usbfs to
+communicate with root hubs and get status reports.  When they do this,
+they may use a transfer_buffer that is smaller than the data returned
+by the HCD, which will lead to a buffer overflow error when
+usb_hcd_poll_rh_status() tries to store the status data.  This was
+discovered by syzbot:
+
+BUG: KASAN: slab-out-of-bounds in memcpy include/linux/fortify-string.h:225 [inline]
+BUG: KASAN: slab-out-of-bounds in usb_hcd_poll_rh_status+0x5f4/0x780 drivers/usb/core/hcd.c:776
+Write of size 2 at addr ffff88801da403c0 by task syz-executor133/4062
+
+This patch fixes the bug by reducing the amount of status data if it
+won't fit in the transfer_buffer.  If some data gets discarded then
+the URB's completion status is set to -EOVERFLOW rather than 0, to let
+the user know what happened.
+
+Reported-and-tested-by: syzbot+3ae6a2b06f131ab9849f@syzkaller.appspotmail.com
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Cc: <stable@vger.kernel.org>
+Link: https://lore.kernel.org/r/Yc+3UIQJ2STbxNua@rowland.harvard.edu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hcd.c |    9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/core/hcd.c
++++ b/drivers/usb/core/hcd.c
+@@ -750,6 +750,7 @@ void usb_hcd_poll_rh_status(struct usb_h
+ {
+       struct urb      *urb;
+       int             length;
++      int             status;
+       unsigned long   flags;
+       char            buffer[6];      /* Any root hubs with > 31 ports? */
+@@ -767,11 +768,17 @@ void usb_hcd_poll_rh_status(struct usb_h
+               if (urb) {
+                       clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
+                       hcd->status_urb = NULL;
++                      if (urb->transfer_buffer_length >= length) {
++                              status = 0;
++                      } else {
++                              status = -EOVERFLOW;
++                              length = urb->transfer_buffer_length;
++                      }
+                       urb->actual_length = length;
+                       memcpy(urb->transfer_buffer, buffer, length);
+                       usb_hcd_unlink_urb_from_ep(hcd, urb);
+-                      usb_hcd_giveback_urb(hcd, urb, 0);
++                      usb_hcd_giveback_urb(hcd, urb, status);
+               } else {
+                       length = 0;
+                       set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);