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

Commit 83d6a26a authored by Hemant Kumar's avatar Hemant Kumar Committed by Mayank Rana
Browse files

USB: gadget: Implement COMPAT_IOCTL for ioctls



32-bit userspace calling into 64-bit kernel
cause different ioctl codes to get generated.
The different ioctl code gets generated
because sizeof is used on mtp and accessory
structures which is different for 32/64
compilation. Because of this, 64-bit kernel
can never execute the right ioctl command.

Implement compat_ioctl to handle such
execution environment.

Change-Id: I26cc10986e28a28eab6f3c65f28f4d2b808112d9
Signed-off-by: default avatarSujeet Kumar <ksujeet@codeaurora.org>
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent e3fe674b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -816,6 +816,9 @@ static const struct file_operations acc_fops = {
	.read = acc_read,
	.write = acc_write,
	.unlocked_ioctl = acc_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = acc_ioctl,
#endif
	.open = acc_open,
	.release = acc_release,
};
+158 −72
Original line number Diff line number Diff line
@@ -983,26 +983,20 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event)
	return ret;
}

static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
static long mtp_send_receive_ioctl(struct file *fp, unsigned int code,
	struct mtp_file_range *mfr)
{
	struct mtp_dev *dev = fp->private_data;
	struct file *filp = NULL;
	struct work_struct *work;
	int ret = -EINVAL;

	if (mtp_lock(&dev->ioctl_excl))
		return -EBUSY;

	switch (code) {
	case MTP_SEND_FILE:
	case MTP_RECEIVE_FILE:
	case MTP_SEND_FILE_WITH_HEADER:
	{
		struct mtp_file_range	mfr;
		struct work_struct *work;

	spin_lock_irq(&dev->lock);
	if (dev->state == STATE_CANCELED) {
			/* report cancelation to userspace */
		/* report cancellation to userspace */
		dev->state = STATE_READY;
		spin_unlock_irq(&dev->lock);
		ret = -ECANCELED;
@@ -1016,12 +1010,8 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
	dev->state = STATE_BUSY;
	spin_unlock_irq(&dev->lock);

		if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
			ret = -EFAULT;
			goto fail;
		}
	/* hold a reference to the file while we are working with it */
		filp = fget(mfr.fd);
	filp = fget(mfr->fd);
	if (!filp) {
		ret = -EBADF;
		goto fail;
@@ -1029,15 +1019,16 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)

	/* write the parameters */
	dev->xfer_file = filp;
		dev->xfer_file_offset = mfr.offset;
		dev->xfer_file_length = mfr.length;
	dev->xfer_file_offset = mfr->offset;
	dev->xfer_file_length = mfr->length;
	/* make sure write is done before parameters are read */
	smp_wmb();

	if (code == MTP_SEND_FILE_WITH_HEADER) {
		work = &dev->send_file_work;
		dev->xfer_send_header = 1;
			dev->xfer_command = mfr.command;
			dev->xfer_transaction_id = mfr.transaction_id;
		dev->xfer_command = mfr->command;
		dev->xfer_transaction_id = mfr->transaction_id;
	} else if (code == MTP_SEND_FILE) {
		work = &dev->send_file_work;
		dev->xfer_send_header = 0;
@@ -1057,11 +1048,40 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
	/* read the result */
	smp_rmb();
	ret = dev->xfer_result;
		break;

fail:
	spin_lock_irq(&dev->lock);
	if (dev->state == STATE_CANCELED)
		ret = -ECANCELED;
	else if (dev->state != STATE_OFFLINE)
		dev->state = STATE_READY;
	spin_unlock_irq(&dev->lock);
out:
	mtp_unlock(&dev->ioctl_excl);
	DBG(dev->cdev, "ioctl returning %d\n", ret);
	return ret;
}
	case MTP_SEND_EVENT:

static long mtp_ioctl(struct file *fp, unsigned int code, unsigned long value)
{
	struct mtp_dev *dev = fp->private_data;
	struct mtp_file_range	mfr;
	struct mtp_event	event;
	int ret = -EINVAL;

	switch (code) {
	case MTP_SEND_FILE:
	case MTP_RECEIVE_FILE:
	case MTP_SEND_FILE_WITH_HEADER:
		if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
			ret = -EFAULT;
			goto fail;
		}
		ret = mtp_send_receive_ioctl(fp, code, &mfr);
	break;
	case MTP_SEND_EVENT:
		if (mtp_lock(&dev->ioctl_excl))
			return -EBUSY;
		/* return here so we don't change dev->state below,
		 * which would interfere with bulk transfer state.
		 */
@@ -1069,22 +1089,85 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
			ret = -EFAULT;
		else
			ret = mtp_send_event(dev, &event);
		goto out;
		mtp_unlock(&dev->ioctl_excl);
	break;
	default:
		DBG(dev->cdev, "unknown ioctl code: %d\n", code);
	}
fail:
	return ret;
}

fail:
	spin_lock_irq(&dev->lock);
	if (dev->state == STATE_CANCELED)
		ret = -ECANCELED;
	else if (dev->state != STATE_OFFLINE)
		dev->state = STATE_READY;
	spin_unlock_irq(&dev->lock);
out:
/*
 * 32 bit userspace calling into 64 bit kernel. handle ioctl code
 * and userspace pointer
 */
#ifdef CONFIG_COMPAT
static long compat_mtp_ioctl(struct file *fp, unsigned int code,
	unsigned long value)
{
	struct mtp_dev *dev = fp->private_data;
	struct mtp_file_range	mfr;
	struct __compat_mtp_file_range	cmfr;
	struct mtp_event	event;
	struct __compat_mtp_event cevent;
	unsigned int cmd;
	bool send_file = false;
	int ret = -EINVAL;

	switch (code) {
	case COMPAT_MTP_SEND_FILE:
		cmd = MTP_SEND_FILE;
		send_file = true;
		break;
	case COMPAT_MTP_RECEIVE_FILE:
		cmd = MTP_RECEIVE_FILE;
		send_file = true;
		break;
	case COMPAT_MTP_SEND_FILE_WITH_HEADER:
		cmd = MTP_SEND_FILE_WITH_HEADER;
		send_file = true;
		break;
	case COMPAT_MTP_SEND_EVENT:
		cmd = MTP_SEND_EVENT;
		break;
	default:
		DBG(dev->cdev, "unknown compat_ioctl code: %d\n", code);
		ret = -ENOIOCTLCMD;
		goto fail;
	}

	if (send_file) {
		if (copy_from_user(&cmfr, (void __user *)value, sizeof(cmfr))) {
			ret = -EFAULT;
			goto fail;
		}
		mfr.fd = cmfr.fd;
		mfr.offset = cmfr.offset;
		mfr.length = cmfr.length;
		mfr.command = cmfr.command;
		mfr.transaction_id = cmfr.transaction_id;
		ret = mtp_send_receive_ioctl(fp, cmd, &mfr);
	} else {
		if (mtp_lock(&dev->ioctl_excl))
			return -EBUSY;
		/* return here so we don't change dev->state below,
		 * which would interfere with bulk transfer state.
		 */
		if (copy_from_user(&cevent, (void __user *)value,
			sizeof(cevent))) {
			ret = -EFAULT;
			goto fail;
		}
		event.length = cevent.length;
		event.data = compat_ptr(cevent.data);
		ret = mtp_send_event(dev, &event);
		mtp_unlock(&dev->ioctl_excl);
	DBG(dev->cdev, "ioctl returning %d\n", ret);
	}
fail:
	return ret;
}
#endif

static int mtp_open(struct inode *ip, struct file *fp)
{
@@ -1114,6 +1197,9 @@ static const struct file_operations mtp_fops = {
	.read = mtp_read,
	.write = mtp_write,
	.unlocked_ioctl = mtp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = compat_mtp_ioctl,
#endif
	.open = mtp_open,
	.release = mtp_release,
};
+30 −0
Original line number Diff line number Diff line
@@ -19,5 +19,35 @@
#define __LINUX_USB_F_MTP_H

#include <uapi/linux/usb/f_mtp.h>
#include <linux/ioctl.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif

#ifdef __KERNEL__

#ifdef CONFIG_COMPAT
struct __compat_mtp_file_range {
	compat_int_t	fd;
	compat_loff_t	offset;
	int64_t		length;
	uint16_t	command;
	uint32_t	transaction_id;
};

struct __compat_mtp_event {
	compat_size_t	length;
	compat_caddr_t	data;
};

#define COMPAT_MTP_SEND_FILE              _IOW('M', 0, \
						struct __compat_mtp_file_range)
#define COMPAT_MTP_RECEIVE_FILE           _IOW('M', 1, \
						struct __compat_mtp_file_range)
#define COMPAT_MTP_SEND_EVENT             _IOW('M', 3, \
						struct __compat_mtp_event)
#define COMPAT_MTP_SEND_FILE_WITH_HEADER  _IOW('M', 4, \
						struct __compat_mtp_file_range)
#endif
#endif
#endif /* __LINUX_USB_F_MTP_H */