Commit fbdbbe6d authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Greg Kroah-Hartman
Browse files

usb: gadget: pch_udc: Check if driver is present before calling ->setup()



Since we have a separate routine for VBUS sense, the interrupt may occur
before gadget driver is present. Hence, ->setup() call may oops the kernel:

[   55.245843] BUG: kernel NULL pointer dereference, address: 00000010
...
[   55.245843] EIP: pch_udc_isr.cold+0x162/0x33f
...
[   55.245843]  <IRQ>
[   55.245843]  ? pch_udc_svc_data_out+0x160/0x160

Check if driver is present before calling ->setup().

Fixes: f646cf94 ("USB device driver of Topcliff PCH")
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20210323153626.54908-2-andriy.shevchenko@linux.intel.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 91356fed
Loading
Loading
Loading
Loading
+18 −10
Original line number Diff line number Diff line
@@ -2298,6 +2298,21 @@ static void pch_udc_svc_data_out(struct pch_udc_dev *dev, int ep_num)
		pch_udc_set_dma(dev, DMA_DIR_RX);
}

static int pch_udc_gadget_setup(struct pch_udc_dev *dev)
	__must_hold(&dev->lock)
{
	int rc;

	/* In some cases we can get an interrupt before driver gets setup */
	if (!dev->driver)
		return -ESHUTDOWN;

	spin_unlock(&dev->lock);
	rc = dev->driver->setup(&dev->gadget, &dev->setup_data);
	spin_lock(&dev->lock);
	return rc;
}

/**
 * pch_udc_svc_control_in() - Handle Control IN endpoint interrupts
 * @dev:	Reference to the device structure
@@ -2369,15 +2384,12 @@ static void pch_udc_svc_control_out(struct pch_udc_dev *dev)
			dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep;
		else /* OUT */
			dev->gadget.ep0 = &ep->ep;
		spin_lock(&dev->lock);
		/* If Mass storage Reset */
		if ((dev->setup_data.bRequestType == 0x21) &&
		    (dev->setup_data.bRequest == 0xFF))
			dev->prot_stall = 0;
		/* call gadget with setup data received */
		setup_supported = dev->driver->setup(&dev->gadget,
						     &dev->setup_data);
		spin_unlock(&dev->lock);
		setup_supported = pch_udc_gadget_setup(dev);

		if (dev->setup_data.bRequestType & USB_DIR_IN) {
			ep->td_data->status = (ep->td_data->status &
@@ -2625,9 +2637,7 @@ static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev)
		dev->ep[i].halted = 0;
	}
	dev->stall = 0;
	spin_unlock(&dev->lock);
	dev->driver->setup(&dev->gadget, &dev->setup_data);
	spin_lock(&dev->lock);
	pch_udc_gadget_setup(dev);
}

/**
@@ -2662,9 +2672,7 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
	dev->stall = 0;

	/* call gadget zero with setup data received */
	spin_unlock(&dev->lock);
	dev->driver->setup(&dev->gadget, &dev->setup_data);
	spin_lock(&dev->lock);
	pch_udc_gadget_setup(dev);
}

/**