Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89cd15c9 authored by Shuah Khan (Samsung OSG)'s avatar Shuah Khan (Samsung OSG) Committed by Greg Kroah-Hartman
Browse files

usb: usbip: Fix BUG: KASAN: slab-out-of-bounds in vhci_hub_control()

commit 81f7567c51ad97668d1c3a48e8ecc482e64d4161 upstream.

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: default avatar <syzbot+bccc1fe10b70fadc78d0@syzkaller.appspotmail.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarShuah Khan (Samsung OSG) <shuah@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6f053e36
Loading
Loading
Loading
Loading
+42 −15
Original line number Diff line number Diff line
@@ -332,8 +332,9 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
	struct vhci_hcd	*vhci_hcd;
	struct vhci	*vhci;
	int             retval = 0;
	int		rhport;
	int		rhport = -1;
	unsigned long	flags;
	bool invalid_rhport = false;

	u32 prev_port_status[VHCI_HC_PORTS];

@@ -348,8 +349,18 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
	usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
			  wIndex);

	/*
	 * 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;

	vhci_hcd = hcd_to_vhci_hcd(hcd);
@@ -359,6 +370,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,

	/* store old status and compare now and old later */
	if (usbip_dbg_flag_vhci_rh) {
		if (!invalid_rhport)
			memcpy(prev_port_status, vhci_hcd->port_status,
				sizeof(prev_port_status));
	}
@@ -368,8 +380,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		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 (hcd->speed == HCD_USB3) {
@@ -429,9 +443,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		break;
	case GetPortStatus:
		usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
		if (wIndex < 1) {
		if (invalid_rhport) {
			pr_err("invalid port number %d\n", wIndex);
			retval = -EPIPE;
			goto error;
		}

		/* we do not care about resume. */
@@ -527,16 +542,20 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
				goto error;
			}

			if (rhport < 0)
			if (invalid_rhport) {
				pr_err("invalid port number %d\n", wIndex);
				goto error;
			}

			vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND;
			break;
		case USB_PORT_FEAT_POWER:
			usbip_dbg_vhci_rh(
				" SetPortFeature: USB_PORT_FEAT_POWER\n");
			if (rhport < 0)
			if (invalid_rhport) {
				pr_err("invalid port number %d\n", wIndex);
				goto error;
			}
			if (hcd->speed == HCD_USB3)
				vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER;
			else
@@ -545,8 +564,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		case USB_PORT_FEAT_BH_PORT_RESET:
			usbip_dbg_vhci_rh(
				" SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n");
			if (rhport < 0)
			if (invalid_rhport) {
				pr_err("invalid port number %d\n", wIndex);
				goto error;
			}
			/* Applicable only for USB3.0 hub */
			if (hcd->speed != HCD_USB3) {
				pr_err("USB_PORT_FEAT_BH_PORT_RESET req not "
@@ -557,8 +578,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		case USB_PORT_FEAT_RESET:
			usbip_dbg_vhci_rh(
				" SetPortFeature: USB_PORT_FEAT_RESET\n");
			if (rhport < 0)
			if (invalid_rhport) {
				pr_err("invalid port number %d\n", wIndex);
				goto error;
			}
			/* if it's already enabled, disable */
			if (hcd->speed == HCD_USB3) {
				vhci_hcd->port_status[rhport] = 0;
@@ -579,8 +602,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		default:
			usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
					  wValue);
			if (rhport < 0)
			if (invalid_rhport) {
				pr_err("invalid port number %d\n", wIndex);
				goto error;
			}
			if (hcd->speed == HCD_USB3) {
				if ((vhci_hcd->port_status[rhport] &
				     USB_SS_PORT_STAT_POWER) != 0) {
@@ -622,7 +647,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
	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],
					      vhci_hcd->port_status[rhport],
					      hcd->speed == HCD_USB3);
@@ -632,8 +657,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,

	spin_unlock_irqrestore(&vhci->lock, flags);

	if ((vhci_hcd->port_status[rhport] & PORT_C_MASK) != 0)
	if (!invalid_rhport &&
	    (vhci_hcd->port_status[rhport] & PORT_C_MASK) != 0) {
		usb_hcd_poll_rh_status(hcd);
	}

	return retval;
}