Commit aa332e67 authored by Al Viro's avatar Al Viro

comedi: get rid of compat_alloc_user_space() mess in COMEDI_INSN compat

Just take copy_from_user() out of do_insn_ioctl() into the caller and
have compat_insn() build a native version and pass it to do_insn_ioctl()
directly.

One difference from the previous commits is that the helper used to
convert 32bit variant to native has two users - compat_insn() and
compat_insnlist().  The latter will be converted in next commit;
for now we simply split the helper in two variants - "userland 32bit
to kernel native" and "userland 32bit to userland native".  The latter
is renamed old get_compat_insn(); it will be gone in the next commit.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 38813876
...@@ -1615,22 +1615,19 @@ static int do_insnlist_ioctl(struct comedi_device *dev, ...@@ -1615,22 +1615,19 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
* data (for reads) to insn->data pointer * data (for reads) to insn->data pointer
*/ */
static int do_insn_ioctl(struct comedi_device *dev, static int do_insn_ioctl(struct comedi_device *dev,
struct comedi_insn __user *arg, void *file) struct comedi_insn *insn, void *file)
{ {
struct comedi_insn insn;
unsigned int *data = NULL; unsigned int *data = NULL;
unsigned int n_data = MIN_SAMPLES; unsigned int n_data = MIN_SAMPLES;
int ret = 0; int ret = 0;
lockdep_assert_held(&dev->mutex); lockdep_assert_held(&dev->mutex);
if (copy_from_user(&insn, arg, sizeof(insn)))
return -EFAULT;
n_data = max(n_data, insn.n); n_data = max(n_data, insn->n);
/* This is where the behavior of insn and insnlist deviate. */ /* This is where the behavior of insn and insnlist deviate. */
if (insn.n > MAX_SAMPLES) { if (insn->n > MAX_SAMPLES) {
insn.n = MAX_SAMPLES; insn->n = MAX_SAMPLES;
n_data = MAX_SAMPLES; n_data = MAX_SAMPLES;
} }
...@@ -1640,26 +1637,26 @@ static int do_insn_ioctl(struct comedi_device *dev, ...@@ -1640,26 +1637,26 @@ static int do_insn_ioctl(struct comedi_device *dev,
goto error; goto error;
} }
if (insn.insn & INSN_MASK_WRITE) { if (insn->insn & INSN_MASK_WRITE) {
if (copy_from_user(data, if (copy_from_user(data,
insn.data, insn->data,
insn.n * sizeof(unsigned int))) { insn->n * sizeof(unsigned int))) {
ret = -EFAULT; ret = -EFAULT;
goto error; goto error;
} }
} }
ret = parse_insn(dev, &insn, data, file); ret = parse_insn(dev, insn, data, file);
if (ret < 0) if (ret < 0)
goto error; goto error;
if (insn.insn & INSN_MASK_READ) { if (insn->insn & INSN_MASK_READ) {
if (copy_to_user(insn.data, if (copy_to_user(insn->data,
data, data,
insn.n * sizeof(unsigned int))) { insn->n * sizeof(unsigned int))) {
ret = -EFAULT; ret = -EFAULT;
goto error; goto error;
} }
} }
ret = insn.n; ret = insn->n;
error: error:
kfree(data); kfree(data);
...@@ -2244,10 +2241,14 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, ...@@ -2244,10 +2241,14 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
(struct comedi_insnlist __user *)arg, (struct comedi_insnlist __user *)arg,
file); file);
break; break;
case COMEDI_INSN: case COMEDI_INSN: {
rc = do_insn_ioctl(dev, (struct comedi_insn __user *)arg, struct comedi_insn insn;
file); if (copy_from_user(&insn, (void __user *)arg, sizeof(insn)))
rc = -EFAULT;
else
rc = do_insn_ioctl(dev, &insn, file);
break; break;
}
case COMEDI_POLL: case COMEDI_POLL:
rc = do_poll_ioctl(dev, arg, file); rc = do_poll_ioctl(dev, arg, file);
break; break;
...@@ -3077,7 +3078,25 @@ static int compat_cmdtest(struct file *file, unsigned long arg) ...@@ -3077,7 +3078,25 @@ static int compat_cmdtest(struct file *file, unsigned long arg)
} }
/* Copy 32-bit insn structure to native insn structure. */ /* Copy 32-bit insn structure to native insn structure. */
static int get_compat_insn(struct comedi_insn __user *insn, static int get_compat_insn(struct comedi_insn *insn,
struct comedi32_insn_struct __user *insn32)
{
struct comedi32_insn_struct v32;
/* Copy insn structure. Ignore the unused members. */
if (copy_from_user(&v32, insn32, sizeof(v32)))
return -EFAULT;
memset(insn, 0, sizeof(*insn));
insn->insn = v32.insn;
insn->n = v32.n;
insn->data = compat_ptr(v32.data);
insn->subdev = v32.subdev;
insn->chanspec = v32.chanspec;
return 0;
}
/* Copy 32-bit insn structure to native insn structure. */
static int __get_compat_insn(struct comedi_insn __user *insn,
struct comedi32_insn_struct __user *insn32) struct comedi32_insn_struct __user *insn32)
{ {
int err; int err;
...@@ -3146,7 +3165,7 @@ static int compat_insnlist(struct file *file, unsigned long arg) ...@@ -3146,7 +3165,7 @@ static int compat_insnlist(struct file *file, unsigned long arg)
/* Copy insn structures. */ /* Copy insn structures. */
for (n = 0; n < n_insns; n++) { for (n = 0; n < n_insns; n++) {
rc = get_compat_insn(&s->insn[n], &insn32[n]); rc = __get_compat_insn(&s->insn[n], &insn32[n]);
if (rc) if (rc)
return rc; return rc;
} }
...@@ -3158,18 +3177,19 @@ static int compat_insnlist(struct file *file, unsigned long arg) ...@@ -3158,18 +3177,19 @@ static int compat_insnlist(struct file *file, unsigned long arg)
/* Handle 32-bit COMEDI_INSN ioctl. */ /* Handle 32-bit COMEDI_INSN ioctl. */
static int compat_insn(struct file *file, unsigned long arg) static int compat_insn(struct file *file, unsigned long arg)
{ {
struct comedi_insn __user *insn; struct comedi_file *cfp = file->private_data;
struct comedi32_insn_struct __user *insn32; struct comedi_device *dev = cfp->dev;
struct comedi_insn insn;
int rc; int rc;
insn32 = compat_ptr(arg); rc = get_compat_insn(&insn, (void __user *)arg);
insn = compat_alloc_user_space(sizeof(*insn));
rc = get_compat_insn(insn, insn32);
if (rc) if (rc)
return rc; return rc;
return comedi_unlocked_ioctl(file, COMEDI_INSN, (unsigned long)insn); mutex_lock(&dev->mutex);
rc = do_insn_ioctl(dev, &insn, file);
mutex_unlock(&dev->mutex);
return rc;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment