Commit f8bc1b2e authored by Spencer E. Olson's avatar Spencer E. Olson Committed by Greg Kroah-Hartman

staging: comedi: change do_insn*_ioctl to allow more samples

Changes do_insn*_ioctl functions to allow for data lengths for each
comedi_insn of up to 2^16.  This patch also changes these functions to only
allocate as much memory as is necessary for each comedi_insn, rather than
allocating a fixed-sized scratch space.

In testing some user-space code for the new INSN_DEVICE_CONFIG_GET_ROUTES
facility with some newer hardware, I discovered that do_insn_ioctl and
do_insnlist_ioctl limited the amount of data that can be passed into the
kernel for insn's to a length of 256.  For some newer hardware, the number
of routes can be greater than 1000.  Working around the old limits (256)
would complicate the user-space/kernel interaction.

The new upper limit is reasonable with current memory available and does
not otherwise impact the memory footprint for any current or otherwise
typical configuration.
Signed-off-by: default avatarSpencer E. Olson <olsonse@umich.edu>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 354ea2ee
...@@ -1501,25 +1501,21 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn, ...@@ -1501,25 +1501,21 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
* data (for reads) to insns[].data pointers * data (for reads) to insns[].data pointers
*/ */
/* arbitrary limits */ /* arbitrary limits */
#define MAX_SAMPLES 256 #define MIN_SAMPLES 16
#define MAX_SAMPLES 65536
static int do_insnlist_ioctl(struct comedi_device *dev, static int do_insnlist_ioctl(struct comedi_device *dev,
struct comedi_insnlist __user *arg, void *file) struct comedi_insnlist __user *arg, void *file)
{ {
struct comedi_insnlist insnlist; struct comedi_insnlist insnlist;
struct comedi_insn *insns = NULL; struct comedi_insn *insns = NULL;
unsigned int *data = NULL; unsigned int *data = NULL;
unsigned int max_n_data_required = MIN_SAMPLES;
int i = 0; int i = 0;
int ret = 0; int ret = 0;
if (copy_from_user(&insnlist, arg, sizeof(insnlist))) if (copy_from_user(&insnlist, arg, sizeof(insnlist)))
return -EFAULT; return -EFAULT;
data = kmalloc_array(MAX_SAMPLES, sizeof(unsigned int), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto error;
}
insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL); insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL);
if (!insns) { if (!insns) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -1533,13 +1529,26 @@ static int do_insnlist_ioctl(struct comedi_device *dev, ...@@ -1533,13 +1529,26 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
goto error; goto error;
} }
for (i = 0; i < insnlist.n_insns; i++) { /* Determine maximum memory needed for all instructions. */
for (i = 0; i < insnlist.n_insns; ++i) {
if (insns[i].n > MAX_SAMPLES) { if (insns[i].n > MAX_SAMPLES) {
dev_dbg(dev->class_dev, dev_dbg(dev->class_dev,
"number of samples too large\n"); "number of samples too large\n");
ret = -EINVAL; ret = -EINVAL;
goto error; goto error;
} }
max_n_data_required = max(max_n_data_required, insns[i].n);
}
/* Allocate scratch space for all instruction data. */
data = kmalloc_array(max_n_data_required, sizeof(unsigned int),
GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto error;
}
for (i = 0; i < insnlist.n_insns; ++i) {
if (insns[i].insn & INSN_MASK_WRITE) { if (insns[i].insn & INSN_MASK_WRITE) {
if (copy_from_user(data, insns[i].data, if (copy_from_user(data, insns[i].data,
insns[i].n * sizeof(unsigned int))) { insns[i].n * sizeof(unsigned int))) {
...@@ -1593,22 +1602,27 @@ static int do_insn_ioctl(struct comedi_device *dev, ...@@ -1593,22 +1602,27 @@ static int do_insn_ioctl(struct comedi_device *dev,
{ {
struct comedi_insn insn; struct comedi_insn insn;
unsigned int *data = NULL; unsigned int *data = NULL;
unsigned int n_data = MIN_SAMPLES;
int ret = 0; int ret = 0;
data = kmalloc_array(MAX_SAMPLES, sizeof(unsigned int), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto error;
}
if (copy_from_user(&insn, arg, sizeof(insn))) { if (copy_from_user(&insn, arg, sizeof(insn))) {
ret = -EFAULT; return -EFAULT;
goto error;
} }
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;
}
data = kmalloc_array(n_data, sizeof(unsigned int), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
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,
......
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