From afd36f12503ae8dcde5e2ce509faeba9d86c985d Mon Sep 17 00:00:00 2001 From: William Wu Date: Thu, 14 Mar 2019 09:27:40 +0800 Subject: usb: dwc3: rockchip: fix NULL pointer dereference in async probe In dwc3_rockchip_async_probe(), if it tries to get hcd in peripheral only mode (dr_mode = "peripheral"), a NULL pointer deference will happen. Because hcd only be allocated and initialized in host mode or otg mode. We can reproduce this issue when set dr_mode to peripheral in DTS, like rk3399pro-npu.dtsi, and get the following panic log on RK1808 EVB: Unable to handle kernel NULL pointer dereference at virtual address 000000b0 pgd = ffffff8008b0b000 [000000b0] *pgd=000000007fffe003, *pud=000000007fffe003, *pmd=0000000000000000 Internal error: Oops: 96000005 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 29 Comm: kworker/u4:1 Not tainted 4.4.167 #493 Hardware name: Rockchip RK1808 EVB V10 Board (DT) Workqueue: events_unbound async_run_entry_fn task: ffffffc07cd29580 task.stack: ffffffc07cd40000 PC is at dwc3_rockchip_async_probe+0x28/0x1c8 LR is at async_run_entry_fn+0x48/0x100 pc : [] lr : [] pstate: 60000045 sp : ffffffc07cd43d10 ... [] dwc3_rockchip_async_probe+0x28/0x1c8 [] async_run_entry_fn+0x48/0x100 [] process_one_work+0x1b8/0x2b8 [] worker_thread+0x304/0x418 [] kthread+0xd0/0xd8 [] ret_from_fork+0x10/0x50 Fixes: f2a2b34e456b ("usb: dwc3: rockchip: use async_schedule for initial dwc3") Change-Id: I740936e43bc4ea2b5a056d6d9dcaf18466006f0c Signed-off-by: William Wu --- drivers/usb/dwc3/dwc3-rockchip.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c index 89cfd02941a8..ef90be82a023 100644 --- a/drivers/usb/dwc3/dwc3-rockchip.c +++ b/drivers/usb/dwc3/dwc3-rockchip.c @@ -57,6 +57,7 @@ struct dwc3_rockchip { struct dwc3 *dwc; struct reset_control *otg_rst; struct extcon_dev *edev; + struct usb_hcd *hcd; struct notifier_block device_nb; struct notifier_block host_nb; struct work_struct otg_work; @@ -642,7 +643,6 @@ static void dwc3_rockchip_async_probe(void *data, async_cookie_t cookie) struct dwc3_rockchip *rockchip = data; struct device *dev = rockchip->dev; struct dwc3 *dwc = rockchip->dwc; - struct usb_hcd *hcd = dev_get_drvdata(&dwc->xhci->dev); int ret; mutex_lock(&rockchip->lock); @@ -669,9 +669,9 @@ static void dwc3_rockchip_async_probe(void *data, async_cookie_t cookie) } if (rockchip->edev || rockchip->dwc->dr_mode == USB_DR_MODE_OTG) { - if (hcd && hcd->state != HC_STATE_HALT) { - usb_remove_hcd(hcd->shared_hcd); - usb_remove_hcd(hcd); + if (rockchip->hcd && rockchip->hcd->state != HC_STATE_HALT) { + usb_remove_hcd(rockchip->hcd->shared_hcd); + usb_remove_hcd(rockchip->hcd); } pm_runtime_set_autosuspend_delay(dwc->dev, @@ -725,7 +725,6 @@ static int dwc3_rockchip_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node, *child; struct platform_device *child_pdev; - struct usb_hcd *hcd = NULL; unsigned int count; int ret; @@ -820,8 +819,8 @@ static int dwc3_rockchip_probe(struct platform_device *pdev) if (rockchip->dwc->dr_mode == USB_DR_MODE_HOST || rockchip->dwc->dr_mode == USB_DR_MODE_OTG) { - hcd = dev_get_drvdata(&rockchip->dwc->xhci->dev); - if (!hcd) { + rockchip->hcd = dev_get_drvdata(&rockchip->dwc->xhci->dev); + if (!rockchip->hcd) { dev_err(dev, "fail to get drvdata hcd\n"); ret = -EPROBE_DEFER; goto err2; -- cgit v1.2.3