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

Commit bd2147a1 authored by Vijayavardhan Vennapusa's avatar Vijayavardhan Vennapusa Committed by Pratham Pratap
Browse files

USB: f_mtp: Perform vfs_write under mutex protection



Use mutex protection to avoid request buffer freed while vfs_write or
copy_to_user happening if disconnect occurs.

Change-Id: Ie353b7495a8b7541f136d7e7ba3fc71db86987e8
Signed-off-by: default avatarVijayavardhan Vennapusa <vvreddy@codeaurora.org>
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent e77f22c4
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
@@ -137,6 +137,7 @@ struct mtp_dev {
	} perf[MAX_ITERATION];
	unsigned int dbg_read_index;
	unsigned int dbg_write_index;
	struct mutex  read_mutex;
};

static struct usb_interface_descriptor mtp_interface_desc = {
@@ -634,11 +635,18 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
	dev->state = STATE_BUSY;
	spin_unlock_irq(&dev->lock);

	mutex_lock(&dev->read_mutex);
	if (dev->state == STATE_OFFLINE) {
		r = -EIO;
		mutex_unlock(&dev->read_mutex);
		goto done;
	}
requeue_req:
	/* queue a request */
	req = dev->rx_req[0];
	req->length = len;
	dev->rx_done = 0;
	mutex_unlock(&dev->read_mutex);
	ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
	if (ret < 0) {
		r = -EIO;
@@ -664,6 +672,7 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
		usb_ep_dequeue(dev->ep_out, req);
		goto done;
	}
	mutex_lock(&dev->read_mutex);
	if (dev->state == STATE_BUSY) {
		/* If we got a 0-len packet, throw it back and try again. */
		if (req->actual == 0)
@@ -677,6 +686,7 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
	} else
		r = -EIO;

	mutex_unlock(&dev->read_mutex);
done:
	spin_lock_irq(&dev->lock);
	if (dev->state == STATE_CANCELED)
@@ -928,6 +938,12 @@ static void receive_file_work(struct work_struct *data)

	while (count > 0 || write_req) {
		if (count > 0) {
			mutex_lock(&dev->read_mutex);
			if (dev->state == STATE_OFFLINE) {
				r = -EIO;
				mutex_unlock(&dev->read_mutex);
				break;
			}
			/* queue a request */
			read_req = dev->rx_req[cur_buf];
			cur_buf = (cur_buf + 1) % RX_REQ_MAX;
@@ -936,6 +952,7 @@ static void receive_file_work(struct work_struct *data)
			read_req->length = mtp_rx_req_len;

			dev->rx_done = 0;
			mutex_unlock(&dev->read_mutex);
			ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
			if (ret < 0) {
				r = -EIO;
@@ -948,16 +965,24 @@ static void receive_file_work(struct work_struct *data)
		if (write_req) {
			DBG(cdev, "rx %pK %d\n", write_req, write_req->actual);
			start_time = ktime_get();
			mutex_lock(&dev->read_mutex);
			if (dev->state == STATE_OFFLINE) {
				r = -EIO;
				mutex_unlock(&dev->read_mutex);
				break;
			}
			ret = vfs_write(filp, write_req->buf, write_req->actual,
				&offset);
			DBG(cdev, "vfs_write %d\n", ret);
			if (ret != write_req->actual) {
				r = -EIO;
				mutex_unlock(&dev->read_mutex);
				if (dev->state != STATE_OFFLINE)
					dev->state = STATE_ERROR;
				usb_ep_dequeue(dev->ep_out, read_req);
				break;
			}
			mutex_unlock(&dev->read_mutex);
			dev->perf[dev->dbg_write_index].vfs_wtime =
				ktime_to_us(ktime_sub(ktime_get(), start_time));
			dev->perf[dev->dbg_write_index].vfs_wbytes = ret;
@@ -985,6 +1010,12 @@ static void receive_file_work(struct work_struct *data)
				break;
			}

			mutex_lock(&dev->read_mutex);
			if (dev->state == STATE_OFFLINE) {
				r = -EIO;
				mutex_unlock(&dev->read_mutex);
				break;
			}
			/* Check if we aligned the size due to MTU constraint */
			if (count < read_req->length)
				read_req->actual = (read_req->actual > count ?
@@ -1005,6 +1036,7 @@ static void receive_file_work(struct work_struct *data)

			write_req = read_req;
			read_req = NULL;
			mutex_unlock(&dev->read_mutex);
		}
	}

@@ -1457,12 +1489,14 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
	int i;
	fi_mtp = container_of(f->fi, struct mtp_instance, func_inst);
	mtp_string_defs[INTERFACE_STRING_INDEX].id = 0;
	mutex_lock(&dev->read_mutex);
	while ((req = mtp_req_get(dev, &dev->tx_idle)))
		mtp_request_free(req, dev->ep_in);
	for (i = 0; i < RX_REQ_MAX; i++)
		mtp_request_free(dev->rx_req[i], dev->ep_out);
	while ((req = mtp_req_get(dev, &dev->intr_idle)))
		mtp_request_free(req, dev->ep_intr);
	mutex_unlock(&dev->read_mutex);
	spin_lock_irq(&dev->lock);
	dev->state = STATE_OFFLINE;
	dev->cdev = NULL;
@@ -1871,6 +1905,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi,
	dev->function.free_func = mtp_free;
	fi->f = &dev->function;

	mutex_init(&dev->read_mutex);
	return &dev->function;
}
EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp);