From 81f7567c51ad97668d1c3a48e8ecc482e64d4161 Mon Sep 17 00:00:00 2001
From: "Shuah Khan (Samsung OSG)" <shuah@kernel.org>
Date: Fri, 5 Oct 2018 16:17:44 -0600
Subject: [PATCH] usb: usbip: Fix BUG: KASAN: slab-out-of-bounds in
vhci_hub_control()
Git-commit: 81f7567c51ad97668d1c3a48e8ecc482e64d4161
Patch-mainline: v4.19
References: bsc#1106110
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>
Signed-off-by: Oliver Neukum <oneukum@suse.com>
---
drivers/usb/usbip/vhci_hcd.c | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -255,8 +255,9 @@ static int vhci_hub_control(struct usb_h
{
struct vhci_hcd *dum;
int retval = 0;
- int rhport;
+ int rhport = -1;
unsigned long flags;
+ bool invalid_rhport = false;
u32 prev_port_status[VHCI_HC_PORTS];
@@ -269,9 +270,19 @@ static int vhci_hub_control(struct usb_h
*/
usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
wIndex);
- if (wIndex > VHCI_HC_PORTS)
- pr_err("invalid port number %d\n", wIndex);
- rhport = ((__u8)(wIndex & 0x00ff)) - 1;
+ /*
+ * wIndex can be 0 for some request types (typeReq). rhport is
+ * in valid range when wIndex >= 1 and < VHCI_HC_PORTS.
+ *
+ * Reference port_status[] only with valid rhport when
+ * invalid_rhport is false.
+ */
+ if (wIndex < 1 || wIndex > VHCI_HC_PORTS) {
+ invalid_rhport = true;
+ if (wIndex > VHCI_HC_PORTS)
+ pr_err("invalid port number %d\n", wIndex);
+ } else
+ rhport = wIndex - 1;
dum = hcd_to_vhci(hcd);
@@ -279,8 +290,9 @@ static int vhci_hub_control(struct usb_h
/* store old status and compare now and old later */
if (usbip_dbg_flag_vhci_rh) {
- memcpy(prev_port_status, dum->port_status,
- sizeof(prev_port_status));
+ if (!invalid_rhport)
+ memcpy(prev_port_status, dum->port_status,
+ sizeof(prev_port_status));
}
switch (typeReq) {
@@ -288,8 +300,10 @@ static int vhci_hub_control(struct usb_h
usbip_dbg_vhci_rh(" ClearHubFeature\n");
break;
case ClearPortFeature:
- if (rhport < 0)
+ if (invalid_rhport) {
+ pr_err("invalid port number %d\n", wIndex);
goto error;
+ }
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if (dum->port_status[rhport] & USB_PORT_STAT_SUSPEND) {
@@ -338,7 +352,7 @@ static int vhci_hub_control(struct usb_h
break;
case GetPortStatus:
usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
- if (wIndex > VHCI_HC_PORTS || wIndex < 1) {
+ if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex);
retval = -EPIPE;
}
@@ -390,8 +404,7 @@ static int vhci_hub_control(struct usb_h
retval = -EPIPE;
break;
case SetPortFeature:
-
- if (rhport < 0)
+ if (invalid_rhport)
goto error;
switch (wValue) {
@@ -433,7 +446,7 @@ error:
if (usbip_dbg_flag_vhci_rh) {
pr_debug("port %d\n", rhport);
/* Only dump valid port status */
- if (rhport >= 0) {
+ if (!invalid_rhport) {
dump_port_status_diff(prev_port_status[rhport],
dum->port_status[rhport]);
}