staging: comedi: comedi_fops: introduce __comedi_get_user_chanlist()

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

Introduce a helper function to handle the memdup_user() and the sanity
checking.

Also, remove the unnecessary dev_dbg() when the memdup_user() or
comedi_check_chanlist() fail.

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
H Hartley Sweeten 2014-03-06 12:02:57 -07:00 committed by Greg Kroah-Hartman
parent 87ece58331
commit c6cd0eefb2

View File

@ -1455,6 +1455,35 @@ static int __comedi_get_user_cmd(struct comedi_device *dev,
return 0; return 0;
} }
static int __comedi_get_user_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned int __user *user_chanlist,
struct comedi_cmd *cmd)
{
unsigned int *chanlist;
int ret;
/* user_chanlist could be NULL for do_cmdtest ioctls */
if (!user_chanlist)
return 0;
chanlist = memdup_user(user_chanlist,
cmd->chanlist_len * sizeof(unsigned int));
if (IS_ERR(chanlist))
return PTR_ERR(chanlist);
/* make sure each element in channel/gain list is valid */
ret = comedi_check_chanlist(s, cmd->chanlist_len, chanlist);
if (ret < 0) {
kfree(chanlist);
return ret;
}
cmd->chanlist = chanlist;
return 0;
}
static int do_cmd_ioctl(struct comedi_device *dev, static int do_cmd_ioctl(struct comedi_device *dev,
struct comedi_cmd __user *arg, void *file) struct comedi_cmd __user *arg, void *file)
{ {
@ -1496,26 +1525,11 @@ static int do_cmd_ioctl(struct comedi_device *dev,
async->cmd = cmd; async->cmd = cmd;
async->cmd.data = NULL; async->cmd.data = NULL;
/* load channel/gain list */
async->cmd.chanlist = memdup_user(user_chanlist,
async->cmd.chanlist_len *
sizeof(int));
if (IS_ERR(async->cmd.chanlist)) {
ret = PTR_ERR(async->cmd.chanlist);
async->cmd.chanlist = NULL;
dev_dbg(dev->class_dev, "memdup_user failed with code %d\n",
ret);
goto cleanup;
}
/* make sure each element in channel/gain list is valid */ /* load channel/gain list */
ret = comedi_check_chanlist(s, ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &async->cmd);
async->cmd.chanlist_len, if (ret)
async->cmd.chanlist);
if (ret < 0) {
dev_dbg(dev->class_dev, "bad chanlist\n");
goto cleanup; goto cleanup;
}
ret = s->do_cmdtest(dev, s, &async->cmd); ret = s->do_cmdtest(dev, s, &async->cmd);
@ -1598,26 +1612,9 @@ static int do_cmdtest_ioctl(struct comedi_device *dev,
s = &dev->subdevices[cmd.subdev]; s = &dev->subdevices[cmd.subdev];
/* load channel/gain list */ /* load channel/gain list */
if (cmd.chanlist) { ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &cmd);
chanlist = memdup_user(user_chanlist, if (ret)
cmd.chanlist_len * sizeof(int)); return ret;
if (IS_ERR(chanlist)) {
ret = PTR_ERR(chanlist);
chanlist = NULL;
dev_dbg(dev->class_dev,
"memdup_user exited with code %d", ret);
goto cleanup;
}
/* make sure each element in channel/gain list is valid */
ret = comedi_check_chanlist(s, cmd.chanlist_len, chanlist);
if (ret < 0) {
dev_dbg(dev->class_dev, "bad chanlist\n");
goto cleanup;
}
cmd.chanlist = chanlist;
}
ret = s->do_cmdtest(dev, s, &cmd); ret = s->do_cmdtest(dev, s, &cmd);
@ -1627,9 +1624,8 @@ static int do_cmdtest_ioctl(struct comedi_device *dev,
if (copy_to_user(arg, &cmd, sizeof(cmd))) { if (copy_to_user(arg, &cmd, sizeof(cmd))) {
dev_dbg(dev->class_dev, "bad cmd address\n"); dev_dbg(dev->class_dev, "bad cmd address\n");
ret = -EFAULT; ret = -EFAULT;
goto cleanup;
} }
cleanup:
kfree(chanlist); kfree(chanlist);
return ret; return ret;