diff mbox series

usb: cdnsp: fix kernel crash when usb_ep_dequeue

Message ID 20230506014242.21912-1-3090101217@zju.edu.cn (mailing list archive)
State New, archived
Headers show
Series usb: cdnsp: fix kernel crash when usb_ep_dequeue | expand

Commit Message

Jing Leng May 6, 2023, 1:42 a.m. UTC
The crash appears in the following situations:
1. Call usb_ep_dequeue without calling usb_ep_queue first
2. Repeated call usb_ep_dequeue

Below is the log of kernel crash when stopping USB FFS device:
[ 1044.732419] Unable to handle kernel NULL pointer dereference
               at virtual address 0000000000000000
...
[ 1044.946689] Call trace:
[ 1044.949133]  cdnsp_trb_in_td.constprop.0+0x4/0xd0 [cdns3]
[ 1044.954545]  cdnsp_gadget_ep_dequeue+0x88/0xf0 [cdns3]
[ 1044.959695]  usb_ep_dequeue+0x18/0x24
[ 1044.963366]  functionfs_unbind+0x2c/0xb4 [usb_f_fs]
[ 1044.968256]  ffs_func_unbind+0x110/0x124 [usb_f_fs]
[ 1044.973144]  purge_configs_funcs+0x9c/0x124 [libcomposite]
[ 1044.978646]  configfs_composite_unbind+0x5c/0xbc [libcomposite]
[ 1044.984581]  usb_gadget_remove_driver+0x60/0xf4
[ 1044.989120]  usb_gadget_unregister_driver+0x70/0xe4
[ 1044.994008]  unregister_gadget_item+0x30/0x58 [libcomposite]
[ 1044.999682]  ffs_data_clear+0x144/0x158 [usb_f_fs]
[ 1045.004483]  ffs_data_closed+0xdc/0x174 [usb_f_fs]
[ 1045.009283]  ffs_ep0_release+0x14/0x24 [usb_f_fs]

Signed-off-by: Jing Leng <3090101217@zju.edu.cn>
---
 drivers/usb/cdns3/cdnsp-gadget.c | 11 +++++++++--
 drivers/usb/cdns3/cdnsp-ring.c   |  5 +++++
 2 files changed, 14 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index fff9ec9c391f..160680a56e1d 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -1072,6 +1072,7 @@  static struct usb_request *cdnsp_gadget_ep_alloc_request(struct usb_ep *ep,
 
 	preq->epnum = pep->number;
 	preq->pep = pep;
+	INIT_LIST_HEAD(&preq->td.td_list);
 
 	trace_cdnsp_alloc_request(preq);
 
@@ -1120,11 +1121,17 @@  static int cdnsp_gadget_ep_queue(struct usb_ep *ep,
 static int cdnsp_gadget_ep_dequeue(struct usb_ep *ep,
 				   struct usb_request *request)
 {
-	struct cdnsp_ep *pep = to_cdnsp_ep(ep);
-	struct cdnsp_device *pdev = pep->pdev;
+	struct cdnsp_ep *pep;
+	struct cdnsp_device *pdev;
 	unsigned long flags;
 	int ret;
 
+	if (!request || !ep)
+		return -EINVAL;
+
+	pep = to_cdnsp_ep(ep);
+	pdev = pep->pdev;
+
 	if (!pep->endpoint.desc) {
 		dev_err(pdev->dev,
 			"%s: can't dequeue to disabled endpoint\n",
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c
index 07f6068342d4..12dc3fb100e9 100644
--- a/drivers/usb/cdns3/cdnsp-ring.c
+++ b/drivers/usb/cdns3/cdnsp-ring.c
@@ -705,6 +705,11 @@  int cdnsp_remove_request(struct cdnsp_device *pdev,
 	trace_cdnsp_remove_request_td(preq);
 
 	cur_td = &preq->td;
+
+	/* Prevent kernel crash caused by re-running usb_ep_dequeue */
+	if (list_empty(&cur_td->td_list))
+		return ret;
+
 	ep_ring = cdnsp_request_to_transfer_ring(pdev, preq);
 
 	/*