Commit 87ece583 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman

staging: comedi: comedi_fops: introduce __comedi_get_user_cmd()

The COMEDI_CMD and COMEDI_CMDTEST ioctl functions both copy the
comedi_cmd passed by the user from __user memory space to kernel
memory space. They then do some basic sanity checking of the cmd
before the subdevice (*do_cmdtest) and (*do_cmd) operations are
called.

Introduce a helper function to handle the copy_from_user() and
do the basic sanity checking.
Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a11f5d2b
...@@ -1416,41 +1416,65 @@ static int do_insn_ioctl(struct comedi_device *dev, ...@@ -1416,41 +1416,65 @@ static int do_insn_ioctl(struct comedi_device *dev,
return ret; return ret;
} }
static int do_cmd_ioctl(struct comedi_device *dev, static int __comedi_get_user_cmd(struct comedi_device *dev,
struct comedi_cmd __user *arg, void *file) struct comedi_cmd __user *arg,
struct comedi_cmd *cmd)
{ {
struct comedi_cmd cmd;
struct comedi_subdevice *s; struct comedi_subdevice *s;
struct comedi_async *async;
int ret = 0;
unsigned int __user *user_chanlist;
if (copy_from_user(&cmd, arg, sizeof(cmd))) { if (copy_from_user(cmd, arg, sizeof(*cmd))) {
dev_dbg(dev->class_dev, "bad cmd address\n"); dev_dbg(dev->class_dev, "bad cmd address\n");
return -EFAULT; return -EFAULT;
} }
/* save user's chanlist pointer so it can be restored later */
user_chanlist = (unsigned int __user *)cmd.chanlist;
if (cmd.subdev >= dev->n_subdevices) { if (cmd->subdev >= dev->n_subdevices) {
dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd.subdev); dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd->subdev);
return -ENODEV; return -ENODEV;
} }
s = &dev->subdevices[cmd.subdev]; s = &dev->subdevices[cmd->subdev];
async = s->async;
if (s->type == COMEDI_SUBD_UNUSED) { if (s->type == COMEDI_SUBD_UNUSED) {
dev_dbg(dev->class_dev, "%d not valid subdevice\n", cmd.subdev); dev_dbg(dev->class_dev, "%d not valid subdevice\n", cmd->subdev);
return -EIO; return -EIO;
} }
if (!s->do_cmd || !s->do_cmdtest || !s->async) { if (!s->do_cmd || !s->do_cmdtest || !s->async) {
dev_dbg(dev->class_dev, dev_dbg(dev->class_dev,
"subdevice %i does not support commands\n", cmd.subdev); "subdevice %d does not support commands\n", cmd->subdev);
return -EIO; return -EIO;
} }
/* make sure channel/gain list isn't too long */
if (cmd->chanlist_len > s->len_chanlist) {
dev_dbg(dev->class_dev, "channel/gain list too long %d > %d\n",
cmd->chanlist_len, s->len_chanlist);
return -EINVAL;
}
return 0;
}
static int do_cmd_ioctl(struct comedi_device *dev,
struct comedi_cmd __user *arg, void *file)
{
struct comedi_cmd cmd;
struct comedi_subdevice *s;
struct comedi_async *async;
unsigned int __user *user_chanlist;
int ret;
/* get the user's cmd and do some simple validation */
ret = __comedi_get_user_cmd(dev, arg, &cmd);
if (ret)
return ret;
/* save user's chanlist pointer so it can be restored later */
user_chanlist = (unsigned int __user *)cmd.chanlist;
s = &dev->subdevices[cmd.subdev];
async = s->async;
/* are we locked? (ioctl lock) */ /* are we locked? (ioctl lock) */
if (s->lock && s->lock != file) { if (s->lock && s->lock != file) {
dev_dbg(dev->class_dev, "subdevice locked\n"); dev_dbg(dev->class_dev, "subdevice locked\n");
...@@ -1463,13 +1487,6 @@ static int do_cmd_ioctl(struct comedi_device *dev, ...@@ -1463,13 +1487,6 @@ static int do_cmd_ioctl(struct comedi_device *dev,
return -EBUSY; return -EBUSY;
} }
/* make sure channel/gain list isn't too long */
if (cmd.chanlist_len > s->len_chanlist) {
dev_dbg(dev->class_dev, "channel/gain list too long %u > %d\n",
cmd.chanlist_len, s->len_chanlist);
return -EINVAL;
}
/* make sure channel/gain list isn't too short */ /* make sure channel/gain list isn't too short */
if (cmd.chanlist_len < 1) { if (cmd.chanlist_len < 1) {
dev_dbg(dev->class_dev, "channel/gain list too short %u < 1\n", dev_dbg(dev->class_dev, "channel/gain list too short %u < 1\n",
...@@ -1566,41 +1583,19 @@ static int do_cmdtest_ioctl(struct comedi_device *dev, ...@@ -1566,41 +1583,19 @@ static int do_cmdtest_ioctl(struct comedi_device *dev,
{ {
struct comedi_cmd cmd; struct comedi_cmd cmd;
struct comedi_subdevice *s; struct comedi_subdevice *s;
int ret = 0;
unsigned int *chanlist = NULL; unsigned int *chanlist = NULL;
unsigned int __user *user_chanlist; unsigned int __user *user_chanlist;
int ret;
/* get the user's cmd and do some simple validation */
ret = __comedi_get_user_cmd(dev, arg, &cmd);
if (ret)
return ret;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
dev_dbg(dev->class_dev, "bad cmd address\n");
return -EFAULT;
}
/* save user's chanlist pointer so it can be restored later */ /* save user's chanlist pointer so it can be restored later */
user_chanlist = (unsigned int __user *)cmd.chanlist; user_chanlist = (unsigned int __user *)cmd.chanlist;
if (cmd.subdev >= dev->n_subdevices) {
dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd.subdev);
return -ENODEV;
}
s = &dev->subdevices[cmd.subdev]; s = &dev->subdevices[cmd.subdev];
if (s->type == COMEDI_SUBD_UNUSED) {
dev_dbg(dev->class_dev, "%d not valid subdevice\n", cmd.subdev);
return -EIO;
}
if (!s->do_cmd || !s->do_cmdtest) {
dev_dbg(dev->class_dev,
"subdevice %i does not support commands\n", cmd.subdev);
return -EIO;
}
/* make sure channel/gain list isn't too long */
if (cmd.chanlist_len > s->len_chanlist) {
dev_dbg(dev->class_dev, "channel/gain list too long %d > %d\n",
cmd.chanlist_len, s->len_chanlist);
ret = -EINVAL;
goto cleanup;
}
/* load channel/gain list */ /* load channel/gain list */
if (cmd.chanlist) { if (cmd.chanlist) {
......
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