[PATCH] usb/g_zero: don't access private data in complete callback on error

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The following scenario:
- use a third party tool
- issue SetConfiguration, start a transfer
- g_zero enqueues 4k reqs. Be rude and stop after a multiple of maxpacketsize
  but before the 4k
- Assume the UDC can handle 4k transfer in one go and will interrupt after the
  4k arrived or a short transfer. That the transfer is still "busy".
- now soft the disconnect the device.
- the UDC gets a SetConfiguration 0 and will disable all endpoints
- disabling an endpoint means cancles all pending transfers.

On DWC3 we issue a cancel transfer command and wait for the command/transfer to
complete. That means we return immediately from ->disable(). The _gadget_ has to
wait for the ->complete() callback. g_zero does not and the result is:

| dwc3 dwc3.0: ep0out: Transfer Complete
| dwc3 dwc3.0: Transfer Complete while ep0out in state 'Setup Phase'
| dwc3 dwc3.0: Inspecting Setup Bytes
| dwc3 dwc3.0: USB_REQ_SET_CONFIGURATION
| zero gadget: reset config
| dwc3 dwc3.0: ep1in: cmd 'End Transfer' params 00000000 00000000 00000000
| dwc3 dwc3.0: Command Complete --> 0

Issue the end transfer command to the udc.

| dwc3 dwc3.0: request dd7e5060 from ep1out completed 0/4096 ===> -108

This completes the one request which in our internal list and not yet
prepared on HW for transfers so we complete it right away.

| zero gadget: high speed config #0: unconfigured
| dwc3 dwc3.0: queueing request dd7e5360 to ep0out length 0, state 'Setup Phase'
| dwc3 dwc3.0: ep0in: Transfer Not Ready
| dwc3 dwc3.0: Transfer Not Ready while ep0in in state 'Setup Phase'
| dwc3 dwc3.0: Control Status
| dwc3 dwc3.0: ep0in: cmd 'Start Transfer' params 00000000 83887000 00000000
| dwc3 dwc3.0: Command Complete --> 0
| dwc3 dwc3.0: ep1in: Endpoint Command Complete
| dwc3 dwc3.0: incomplete IN transfer ep1in
| dwc3 dwc3.0: request dd7e5420 from ep1in completed 0/4096 ===> -104
This is the response to our "End Transfer" command.

| Unable to handle kernel NULL pointer dereference at virtual address 00000014
dereference of ep->driver_data which is NULL due to cleanup in disable by
source sink.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
---
 drivers/usb/gadget/f_loopback.c   |    5 +++--
 drivers/usb/gadget/f_sourcesink.c |   13 +++++--------
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index ca660d4..6f3e14a 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -243,7 +243,7 @@ loopback_unbind(struct usb_configuration *c, struct usb_function *f)
 static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
 {
 	struct f_loopback	*loop = ep->driver_data;
-	struct usb_composite_dev *cdev = loop->function.config->cdev;
+	struct usb_composite_dev *cdev;
 	int			status = req->status;
 
 	switch (status) {
@@ -258,6 +258,7 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
 				return;
 
 			/* "should never get here" */
+			cdev = loop->function.config->cdev;
 			ERROR(cdev, "can't loop %s to %s: %d\n",
 				ep->name, loop->in_ep->name,
 				status);
@@ -273,7 +274,7 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
 		/* FALLTHROUGH */
 
 	default:
-		ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
+		pr_err("%s loop complete --> %d, %d/%d\n", ep->name,
 				status, req->actual, req->length);
 		/* FALLTHROUGH */
 
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
index e18b4f5..5c67247 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -306,7 +306,7 @@ static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
 static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
 {
 	struct f_sourcesink	*ss = ep->driver_data;
-	struct usb_composite_dev *cdev = ss->function.config->cdev;
+	struct usb_composite_dev *cdev;
 	int			status = req->status;
 
 	switch (status) {
@@ -323,10 +323,8 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
 	case -ECONNABORTED:		/* hardware forced ep reset */
 	case -ECONNRESET:		/* request dequeued */
 	case -ESHUTDOWN:		/* disconnect from host */
-		VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
+		pr_debug("%s gone (%d), %d/%d\n", ep->name, status,
 				req->actual, req->length);
-		if (ep == ss->out_ep)
-			check_read_data(ss, req);
 		free_ep_req(ep, req);
 		return;
 
@@ -335,17 +333,16 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
 					 * buffer.
 					 */
 	default:
-#if 1
-		DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
+		pr_debug("%s complete --> %d, %d/%d\n", ep->name,
 				status, req->actual, req->length);
-#endif
 	case -EREMOTEIO:		/* short read */
 		break;
 	}
 
 	status = usb_ep_queue(ep, req, GFP_ATOMIC);
 	if (status) {
-		ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
+		cdev = loop->function.config->cdev;
+		ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
 				ep->name, req->length, status);
 		usb_ep_set_halt(ep);
 		/* FIXME recover later ... somehow */
-- 
1.7.8.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux