aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShuah Khan (Samsung OSG) <shuah@kernel.org>2018-10-05 18:17:44 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-10-09 10:13:42 -0400
commit81f7567c51ad97668d1c3a48e8ecc482e64d4161 (patch)
treeb6ff61e20dad90a6bb9af8bb420ffd71e26a8d90
parent4b0aaacee51eb6592a03fdefd5ce97558518e291 (diff)
usb: usbip: Fix BUG: KASAN: slab-out-of-bounds in vhci_hub_control()
vhci_hub_control() accesses port_status array with out of bounds port value. Fix it to reference port_status[] only with a valid rhport value when invalid_rhport flag is true. The invalid_rhport flag is set early on after detecting in port value is within the bounds or not. The following is used reproduce the problem and verify the fix: C reproducer: https://syzkaller.appspot.com/x/repro.c?x=14ed8ab6400000 Reported-by: syzbot+bccc1fe10b70fadc78d0@syzkaller.appspotmail.com Cc: stable <stable@vger.kernel.org> Signed-off-by: Shuah Khan (Samsung OSG) <shuah@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/usbip/vhci_hcd.c57
1 files changed, 42 insertions, 15 deletions
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index d11f3f8dad40..1e592ec94ba4 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -318,8 +318,9 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
318 struct vhci_hcd *vhci_hcd; 318 struct vhci_hcd *vhci_hcd;
319 struct vhci *vhci; 319 struct vhci *vhci;
320 int retval = 0; 320 int retval = 0;
321 int rhport; 321 int rhport = -1;
322 unsigned long flags; 322 unsigned long flags;
323 bool invalid_rhport = false;
323 324
324 u32 prev_port_status[VHCI_HC_PORTS]; 325 u32 prev_port_status[VHCI_HC_PORTS];
325 326
@@ -334,9 +335,19 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
334 usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, 335 usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
335 wIndex); 336 wIndex);
336 337
337 if (wIndex > VHCI_HC_PORTS) 338 /*
338 pr_err("invalid port number %d\n", wIndex); 339 * wIndex can be 0 for some request types (typeReq). rhport is
339 rhport = wIndex - 1; 340 * in valid range when wIndex >= 1 and < VHCI_HC_PORTS.
341 *
342 * Reference port_status[] only with valid rhport when
343 * invalid_rhport is false.
344 */
345 if (wIndex < 1 || wIndex > VHCI_HC_PORTS) {
346 invalid_rhport = true;
347 if (wIndex > VHCI_HC_PORTS)
348 pr_err("invalid port number %d\n", wIndex);
349 } else
350 rhport = wIndex - 1;
340 351
341 vhci_hcd = hcd_to_vhci_hcd(hcd); 352 vhci_hcd = hcd_to_vhci_hcd(hcd);
342 vhci = vhci_hcd->vhci; 353 vhci = vhci_hcd->vhci;
@@ -345,8 +356,9 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
345 356
346 /* store old status and compare now and old later */ 357 /* store old status and compare now and old later */
347 if (usbip_dbg_flag_vhci_rh) { 358 if (usbip_dbg_flag_vhci_rh) {
348 memcpy(prev_port_status, vhci_hcd->port_status, 359 if (!invalid_rhport)
349 sizeof(prev_port_status)); 360 memcpy(prev_port_status, vhci_hcd->port_status,
361 sizeof(prev_port_status));
350 } 362 }
351 363
352 switch (typeReq) { 364 switch (typeReq) {
@@ -354,8 +366,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
354 usbip_dbg_vhci_rh(" ClearHubFeature\n"); 366 usbip_dbg_vhci_rh(" ClearHubFeature\n");
355 break; 367 break;
356 case ClearPortFeature: 368 case ClearPortFeature:
357 if (rhport < 0) 369 if (invalid_rhport) {
370 pr_err("invalid port number %d\n", wIndex);
358 goto error; 371 goto error;
372 }
359 switch (wValue) { 373 switch (wValue) {
360 case USB_PORT_FEAT_SUSPEND: 374 case USB_PORT_FEAT_SUSPEND:
361 if (hcd->speed == HCD_USB3) { 375 if (hcd->speed == HCD_USB3) {
@@ -415,9 +429,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
415 break; 429 break;
416 case GetPortStatus: 430 case GetPortStatus:
417 usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); 431 usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
418 if (wIndex < 1) { 432 if (invalid_rhport) {
419 pr_err("invalid port number %d\n", wIndex); 433 pr_err("invalid port number %d\n", wIndex);
420 retval = -EPIPE; 434 retval = -EPIPE;
435 goto error;
421 } 436 }
422 437
423 /* we do not care about resume. */ 438 /* we do not care about resume. */
@@ -513,16 +528,20 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
513 goto error; 528 goto error;
514 } 529 }
515 530
516 if (rhport < 0) 531 if (invalid_rhport) {
532 pr_err("invalid port number %d\n", wIndex);
517 goto error; 533 goto error;
534 }
518 535
519 vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND; 536 vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND;
520 break; 537 break;
521 case USB_PORT_FEAT_POWER: 538 case USB_PORT_FEAT_POWER:
522 usbip_dbg_vhci_rh( 539 usbip_dbg_vhci_rh(
523 " SetPortFeature: USB_PORT_FEAT_POWER\n"); 540 " SetPortFeature: USB_PORT_FEAT_POWER\n");
524 if (rhport < 0) 541 if (invalid_rhport) {
542 pr_err("invalid port number %d\n", wIndex);
525 goto error; 543 goto error;
544 }
526 if (hcd->speed == HCD_USB3) 545 if (hcd->speed == HCD_USB3)
527 vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER; 546 vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER;
528 else 547 else
@@ -531,8 +550,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
531 case USB_PORT_FEAT_BH_PORT_RESET: 550 case USB_PORT_FEAT_BH_PORT_RESET:
532 usbip_dbg_vhci_rh( 551 usbip_dbg_vhci_rh(
533 " SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n"); 552 " SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n");
534 if (rhport < 0) 553 if (invalid_rhport) {
554 pr_err("invalid port number %d\n", wIndex);
535 goto error; 555 goto error;
556 }
536 /* Applicable only for USB3.0 hub */ 557 /* Applicable only for USB3.0 hub */
537 if (hcd->speed != HCD_USB3) { 558 if (hcd->speed != HCD_USB3) {
538 pr_err("USB_PORT_FEAT_BH_PORT_RESET req not " 559 pr_err("USB_PORT_FEAT_BH_PORT_RESET req not "
@@ -543,8 +564,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
543 case USB_PORT_FEAT_RESET: 564 case USB_PORT_FEAT_RESET:
544 usbip_dbg_vhci_rh( 565 usbip_dbg_vhci_rh(
545 " SetPortFeature: USB_PORT_FEAT_RESET\n"); 566 " SetPortFeature: USB_PORT_FEAT_RESET\n");
546 if (rhport < 0) 567 if (invalid_rhport) {
568 pr_err("invalid port number %d\n", wIndex);
547 goto error; 569 goto error;
570 }
548 /* if it's already enabled, disable */ 571 /* if it's already enabled, disable */
549 if (hcd->speed == HCD_USB3) { 572 if (hcd->speed == HCD_USB3) {
550 vhci_hcd->port_status[rhport] = 0; 573 vhci_hcd->port_status[rhport] = 0;
@@ -565,8 +588,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
565 default: 588 default:
566 usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", 589 usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
567 wValue); 590 wValue);
568 if (rhport < 0) 591 if (invalid_rhport) {
592 pr_err("invalid port number %d\n", wIndex);
569 goto error; 593 goto error;
594 }
570 if (hcd->speed == HCD_USB3) { 595 if (hcd->speed == HCD_USB3) {
571 if ((vhci_hcd->port_status[rhport] & 596 if ((vhci_hcd->port_status[rhport] &
572 USB_SS_PORT_STAT_POWER) != 0) { 597 USB_SS_PORT_STAT_POWER) != 0) {
@@ -608,7 +633,7 @@ error:
608 if (usbip_dbg_flag_vhci_rh) { 633 if (usbip_dbg_flag_vhci_rh) {
609 pr_debug("port %d\n", rhport); 634 pr_debug("port %d\n", rhport);
610 /* Only dump valid port status */ 635 /* Only dump valid port status */
611 if (rhport >= 0) { 636 if (!invalid_rhport) {
612 dump_port_status_diff(prev_port_status[rhport], 637 dump_port_status_diff(prev_port_status[rhport],
613 vhci_hcd->port_status[rhport], 638 vhci_hcd->port_status[rhport],
614 hcd->speed == HCD_USB3); 639 hcd->speed == HCD_USB3);
@@ -618,8 +643,10 @@ error:
618 643
619 spin_unlock_irqrestore(&vhci->lock, flags); 644 spin_unlock_irqrestore(&vhci->lock, flags);
620 645
621 if ((vhci_hcd->port_status[rhport] & PORT_C_MASK) != 0) 646 if (!invalid_rhport &&
647 (vhci_hcd->port_status[rhport] & PORT_C_MASK) != 0) {
622 usb_hcd_poll_rh_status(hcd); 648 usb_hcd_poll_rh_status(hcd);
649 }
623 650
624 return retval; 651 return retval;
625} 652}