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 : [<ffffff80083adf5c>] lr : [<ffffff80080b445c>] pstate: 60000045
sp : ffffffc07cd43d10
...
[<ffffff80083adf5c>] dwc3_rockchip_async_probe+0x28/0x1c8
[<ffffff80080b445c>] async_run_entry_fn+0x48/0x100
[<ffffff80080acca8>] process_one_work+0x1b8/0x2b8
[<ffffff80080ad94c>] worker_thread+0x304/0x418
[<ffffff80080b206c>] kthread+0xd0/0xd8
[<ffffff8008082e80>] ret_from_fork+0x10/0x50

Fixes: f2a2b34e45 ("usb: dwc3: rockchip: use async_schedule for initial dwc3")
Change-Id: I740936e43bc4ea2b5a056d6d9dcaf18466006f0c
Signed-off-by: William Wu <william.wu@rock-chips.com>
This commit is contained in:
William Wu
2019-03-14 09:27:40 +08:00
committed by Tao Huang
parent b1d6e7cbf8
commit c16f9cf81e

View File

@@ -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;