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

Commit 22d6ccbd authored by Vijayavardhan Vennapusa's avatar Vijayavardhan Vennapusa
Browse files

USB: f_qc_rndis: Add spinlock protection whereever required



Add spinlock protection in rndis_qc_command_complete() and
rndis_qc_response_complete() to avoid races with disable and
unbind() function calls. Otherwise it results in crash or using
freed memory.

Change-Id: Ida99de70a541ba12a8a8610b1c6fa717e42d865c
Signed-off-by: default avatarVijayavardhan Vennapusa <vvreddy@codeaurora.org>
parent 0940fb6a
Loading
Loading
Loading
Loading
+39 −13
Original line number Original line Diff line number Diff line
@@ -493,17 +493,26 @@ static void rndis_qc_response_available(void *_rndis)
static void rndis_qc_response_complete(struct usb_ep *ep,
static void rndis_qc_response_complete(struct usb_ep *ep,
					struct usb_request *req)
					struct usb_request *req)
{
{
	struct f_rndis_qc		*rndis = req->context;
	struct f_rndis_qc		*rndis;
	int				status = req->status;
	int				status = req->status;
	struct usb_composite_dev	*cdev;
	struct usb_composite_dev	*cdev;
	struct usb_ep *notify_ep;

	spin_lock(&rndis_lock);
	rndis = _rndis_qc;
	if (!rndis || !rndis->notify || !rndis->notify->driver_data) {
		spin_unlock(&rndis_lock);
		return;
	}


	if (!rndis->port.func.config || !rndis->port.func.config->cdev) {
	if (!rndis->port.func.config || !rndis->port.func.config->cdev) {
		pr_err("%s(): cdev or config is NULL.\n", __func__);
		pr_err("%s(): cdev or config is NULL.\n", __func__);
		spin_unlock(&rndis_lock);
		return;
		return;
	} else {
		cdev = rndis->port.func.config->cdev;
	}
	}


	cdev = rndis->port.func.config->cdev;

	/* after TX:
	/* after TX:
	 *  - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
	 *  - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
	 *  - RNDIS_RESPONSE_AVAILABLE (status/irq)
	 *  - RNDIS_RESPONSE_AVAILABLE (status/irq)
@@ -513,7 +522,7 @@ static void rndis_qc_response_complete(struct usb_ep *ep,
	case -ESHUTDOWN:
	case -ESHUTDOWN:
		/* connection gone */
		/* connection gone */
		atomic_set(&rndis->notify_count, 0);
		atomic_set(&rndis->notify_count, 0);
		break;
		goto out;
	default:
	default:
		pr_info("RNDIS %s response error %d, %d/%d\n",
		pr_info("RNDIS %s response error %d, %d/%d\n",
			ep->name, status,
			ep->name, status,
@@ -521,20 +530,30 @@ static void rndis_qc_response_complete(struct usb_ep *ep,
		/* FALLTHROUGH */
		/* FALLTHROUGH */
	case 0:
	case 0:
		if (ep != rndis->notify)
		if (ep != rndis->notify)
			break;
			goto out;


		/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
		/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
		 * notifications by resending until we're done
		 * notifications by resending until we're done
		 */
		 */
		if (atomic_dec_and_test(&rndis->notify_count))
		if (atomic_dec_and_test(&rndis->notify_count))
			break;
			goto out;
		status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
		notify_ep = rndis->notify;
		spin_unlock(&rndis_lock);
		status = usb_ep_queue(notify_ep, req, GFP_ATOMIC);
		if (status) {
		if (status) {
			atomic_dec(&rndis->notify_count);
			spin_lock(&rndis_lock);
			if (!_rndis_qc)
				goto out;
			atomic_dec(&_rndis_qc->notify_count);
			DBG(cdev, "notify/1 --> %d\n", status);
			DBG(cdev, "notify/1 --> %d\n", status);
			spin_unlock(&rndis_lock);
		}
		}
		break;
	}
	}

	return;

out:
	spin_unlock(&rndis_lock);
}
}


static void rndis_qc_command_complete(struct usb_ep *ep,
static void rndis_qc_command_complete(struct usb_ep *ep,
@@ -545,6 +564,13 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
	rndis_init_msg_type		*buf;
	rndis_init_msg_type		*buf;
	u32		ul_max_xfer_size, dl_max_xfer_size;
	u32		ul_max_xfer_size, dl_max_xfer_size;


	spin_lock(&rndis_lock);
	rndis = _rndis_qc;
	if (!rndis || !rndis->notify || !rndis->notify->driver_data) {
		spin_unlock(&rndis_lock);
		return;
	}

	/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
	/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
	status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
	status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
	if (status < 0)
	if (status < 0)
@@ -573,6 +599,7 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
				rndis_get_dl_max_xfer_size(rndis->config);
				rndis_get_dl_max_xfer_size(rndis->config);
		u_bam_data_set_dl_max_xfer_size(dl_max_xfer_size);
		u_bam_data_set_dl_max_xfer_size(dl_max_xfer_size);
	}
	}
	spin_unlock(&rndis_lock);
}
}


static int
static int
@@ -751,13 +778,16 @@ static void rndis_qc_disable(struct usb_function *f)
{
{
	struct f_rndis_qc		*rndis = func_to_rndis_qc(f);
	struct f_rndis_qc		*rndis = func_to_rndis_qc(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	struct usb_composite_dev *cdev = f->config->cdev;
	unsigned long flags;


	if (!rndis->notify->driver_data)
	if (!rndis->notify->driver_data)
		return;
		return;


	pr_info("rndis deactivated\n");
	pr_info("rndis deactivated\n");


	spin_lock_irqsave(&rndis_lock, flags);
	rndis_uninit(rndis->config);
	rndis_uninit(rndis->config);
	spin_unlock_irqrestore(&rndis_lock, flags);
	bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS, rndis->port_num);
	bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS, rndis->port_num);
	if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA)
	if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA)
		gether_qc_disconnect_name(&rndis->port, "rndis0");
		gether_qc_disconnect_name(&rndis->port, "rndis0");
@@ -1079,18 +1109,14 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f)
void rndis_ipa_reset_trigger(void)
void rndis_ipa_reset_trigger(void)
{
{
	struct f_rndis_qc *rndis;
	struct f_rndis_qc *rndis;
	unsigned long flags;


	spin_lock_irqsave(&rndis_lock, flags);
	rndis = _rndis_qc;
	rndis = _rndis_qc;
	if (!rndis) {
	if (!rndis) {
		pr_err("%s: No RNDIS instance", __func__);
		pr_err("%s: No RNDIS instance", __func__);
		spin_unlock_irqrestore(&rndis_lock, flags);
		return;
		return;
	}
	}


	rndis->net_ready_trigger = false;
	rndis->net_ready_trigger = false;
	spin_unlock_irqrestore(&rndis_lock, flags);
}
}


/*
/*