MLK-10994-5 usb: chipidea: otg: fix NULL pointer dereference when unload module
authorPeter Chen <peter.chen@freescale.com>
Wed, 3 Jun 2015 11:36:29 +0000 (19:36 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:47:16 +0000 (14:47 -0500)
With commit "95b62fe MLK-10750 usb: chipidea: otg: remove otg fsm
before destroy gdaget and host", the otg fsm will be removed first,
but when the host is removing, it will trigger pcd interrupt, and otg
work is still queued to ci_otg workqueue(otg state is OTG_STATE_A_HOST),
but at that time, ci_otg workqueue has been destroyed.

In this commit, we make sure the otg work is not queued if ci->wq is NULL,
and keep otg state is OTG_STATE_UNDEFINED after otg fsm has been removed.

The NULL pointer deference error like belows:

Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = a873c000
[00000000] *pgd=a90f9831, *pte=00000000, *ppte=00000000
Internal error: Oops: 17 [#1] PREEMPT SMP ARM
Modules linked in: usb_f_ecm u_ether libcomposite configfs ci_hdrc_imx(-) ci_hdrc udc_core ehci_hcd mxc_vadc mx6s_capture mxc_dcic ov5640_camera usbmisc_imx phy_mxs_usb evbug [last unloaded: usb_f_rndis]
CPU: 0 PID: 162 Comm: udevd Not tainted 3.14.38-02187-g5639985-dirty #160
task: a863e880 ti: a872e000 task.ti: a872e000
PC is at __queue_work+0x68/0x268
LR is at __queue_work+0x68/0x268
pc : [<80045060>]    lr : [<80045060>]    psr: 600e0193
sp : a872fed0  ip : 00000000  fp : 00000000
r10: 00000004  r9 : a872e000  r8 : 0000004b
r7 : 80e4c804  r6 : a9295000  r5 : a87f8294  r4 : 00000000
r3 : a80690e0  r2 : 00000000  r1 : a8069108  r0 : a8003400
Flags: nZCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 10c53c7d  Table: a873c04a  DAC: 00000015
Process udevd (pid: 162, stack limit = 0xa872e238)
Stack: (0xa872fed0 to 0xa8730000)
fec0:                                     00ff3d34 80743750 00000000 600e0193
fee0: a87f8294 a9295000 00000004 0000004b a8008900 80eb51ff 00ff3d34 800452a8
ff00: 00000000 a87f8010 00000000 00000000 00000000 7f060ac0 a8dfced0 a87f8010
ff20: 00002e20 7f059730 a8e7e2c0 a800895c 00000000 8006c1b4 80e46080 80e40458
ff40: a8008900 a800895c a8e7e2c0 c0802100 00fc40e8 00000000 00000048 8006c320
ff60: a8008900 a800895c 00000000 8006f1c4 8006f140 0000004b 0000004b 8006b92c
ff80: 80e40e54 8000f9cc c080210c 80e4c970 a872ffb0 8000856c 00fc4308 76d9c890
ffa0: 600e0010 ffffffff 7edef480 800130bc 00fc4308 00ff3d70 00000000 000000ff
ffc0: 00000671 00000000 00ff3b90 7edef480 00fc40e8 00000000 00000048 00ff3d34
ffe0: 000321b8 7edef140 000240a0 76d9c890 600e0010 ffffffff abf5e821 abf5ec21
[<80045060>] (__queue_work) from [<800452a8>] (queue_work_on+0x48/0x54)
[<800452a8>] (queue_work_on) from [<7f060ac0>] (ci_otg_fsm_irq+0x108/0x310 [ci_hdrc])
[<7f060ac0>] (ci_otg_fsm_irq [ci_hdrc]) from [<7f059730>] (ci_irq+0x94/0x158 [ci_hdrc])
[<7f059730>] (ci_irq [ci_hdrc]) from [<8006c1b4>] (handle_irq_event_percpu+0x50/0x180)
[<8006c1b4>] (handle_irq_event_percpu) from [<8006c320>] (handle_irq_event+0x3c/0x5c)
[<8006c320>] (handle_irq_event) from [<8006f1c4>] (handle_fasteoi_irq+0x84/0x14c)
[<8006f1c4>] (handle_fasteoi_irq) from [<8006b92c>] (generic_handle_irq+0x2c/0x3c)
[<8006b92c>] (generic_handle_irq) from [<8000f9cc>] (handle_IRQ+0x40/0x90)
[<8000f9cc>] (handle_IRQ) from [<8000856c>] (gic_handle_irq+0x2c/0x5c)
[<8000856c>] (gic_handle_irq) from [<800130bc>] (__irq_usr+0x3c/0x60)
Exception stack(0xa872ffb0 to 0xa872fff8)
ffa0:                                     00fc4308 00ff3d70 00000000 000000ff
ffc0: 00000671 00000000 00ff3b90 7edef480 00fc40e8 00000000 00000048 00ff3d34
ffe0: 000321b8 7edef140 000240a0 76d9c890 600e0010 ffffffff
Code: e5964084 e0844003 e1a00005 ebfffa14 (e5943000)
---[ end trace 53dc25e918ff7216 ]---
Kernel panic - not syncing: Fatal exception in interrupt

Signed-off-by: Peter Chen <peter.chen@freescale.com>
(cherry picked from commit c5b8111e4178387e1a6e9c700fdf887a944a5129)

drivers/usb/chipidea/otg.c
drivers/usb/chipidea/otg.h
drivers/usb/chipidea/otg_fsm.c

index 5532142..1d2d99c 100644 (file)
@@ -330,6 +330,7 @@ void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
        if (ci->wq) {
                flush_workqueue(ci->wq);
                destroy_workqueue(ci->wq);
+               ci->wq = NULL;
        }
        /* Disable all OTG irq and clear status */
        hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
index b8b4bbd..43296cd 100644 (file)
@@ -21,8 +21,11 @@ void ci_handle_id_switch(struct ci_hdrc *ci);
 void ci_handle_vbus_connected(struct ci_hdrc *ci);
 static inline void ci_otg_queue_work(struct ci_hdrc *ci)
 {
+       WARN_ON(!ci->wq);
+
        disable_irq_nosync(ci->irq);
-       queue_work(ci->wq, &ci->work);
+       if (ci->wq)
+               queue_work(ci->wq, &ci->work);
 }
 
 #endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
index e52ea80..170faa4 100644 (file)
@@ -945,6 +945,7 @@ void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci)
                otg_drv_vbus(&ci->fsm, 0);
 
        sysfs_remove_group(&ci->dev->kobj, &inputs_attr_group);
+       ci->fsm.otg->state = OTG_STATE_UNDEFINED;
 }
 
 /* Restart OTG fsm if resume from power lost */