Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dde93857 authored by Jack Pham's avatar Jack Pham
Browse files

usb: gadget: diag: fix read/write race with unbind



Prevent against potential race condition when unbind occurs
while a usb_diag_read or usb_diag_write is being issued from
another thread. Do this by keeping a local copy of the in
and out usb_ep pointers, and also by doing a last-chance
equality check between ctxt and the usb_diag_ch priv_usb member
to determine if the connection is still valid. This ensures
that the contents of the ctxt pointer don't get reallocated
when in the midst of an unbind/rebind.

CRs-Fixed: 652651
Change-Id: I75f1dafb4aa688dd068bd58e2c0a72a3c8193d10
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
parent c82bee2d
Loading
Loading
Loading
Loading
+24 −4
Original line number Diff line number Diff line
@@ -422,6 +422,7 @@ int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)
	struct diag_context *ctxt = ch->priv_usb;
	unsigned long flags;
	struct usb_request *req;
	struct usb_ep *out;
	static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);

	if (!ctxt)
@@ -429,11 +430,13 @@ int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)

	spin_lock_irqsave(&ctxt->lock, flags);

	if (!ctxt->configured) {
	if (!ctxt->configured || !ctxt->out) {
		spin_unlock_irqrestore(&ctxt->lock, flags);
		return -EIO;
	}

	out = ctxt->out;

	if (list_empty(&ctxt->read_pool)) {
		spin_unlock_irqrestore(&ctxt->lock, flags);
		ERROR(ctxt->cdev, "%s: no requests available\n", __func__);
@@ -447,7 +450,14 @@ int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)
	req->buf = d_req->buf;
	req->length = d_req->length;
	req->context = d_req;
	if (usb_ep_queue(ctxt->out, req, GFP_ATOMIC)) {

	/* make sure context is still valid after releasing lock */
	if (ctxt != ch->priv_usb) {
		usb_ep_free_request(out, req);
		return -EIO;
	}

	if (usb_ep_queue(out, req, GFP_ATOMIC)) {
		/* If error add the link to linked list again*/
		spin_lock_irqsave(&ctxt->lock, flags);
		list_add_tail(&req->list, &ctxt->read_pool);
@@ -481,6 +491,7 @@ int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)
	struct diag_context *ctxt = ch->priv_usb;
	unsigned long flags;
	struct usb_request *req = NULL;
	struct usb_ep *in;
	static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);

	if (!ctxt)
@@ -488,11 +499,13 @@ int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)

	spin_lock_irqsave(&ctxt->lock, flags);

	if (!ctxt->configured) {
	if (!ctxt->configured || !ctxt->in) {
		spin_unlock_irqrestore(&ctxt->lock, flags);
		return -EIO;
	}

	in = ctxt->in;

	if (list_empty(&ctxt->write_pool)) {
		spin_unlock_irqrestore(&ctxt->lock, flags);
		ERROR(ctxt->cdev, "%s: no requests available\n", __func__);
@@ -506,7 +519,14 @@ int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)
	req->buf = d_req->buf;
	req->length = d_req->length;
	req->context = d_req;
	if (usb_ep_queue(ctxt->in, req, GFP_ATOMIC)) {

	/* make sure context is still valid after releasing lock */
	if (ctxt != ch->priv_usb) {
		usb_ep_free_request(in, req);
		return -EIO;
	}

	if (usb_ep_queue(in, req, GFP_ATOMIC)) {
		/* If error add the link to linked list again*/
		spin_lock_irqsave(&ctxt->lock, flags);
		list_add_tail(&req->list, &ctxt->write_pool);