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: Spencer E. Olson <olso...@umich.edu>
---
Implements Ian's suggestions to:
1) Provide a minimum amount of space to allocate (16*sizeof(uint)) to protect
   drivers that do not (yet) check the size of the instruction data (Ian has
   submitted several patches fixing this for other drivers already).  In case
   insn.n does not get set, this minimum amount also avoids trying to allocate
   zero-length data.
2) Allocate the maximum required space needed for any of the instructions in an
   instruction list before executing the list of instructions (this, rather than
   allocating and freeing memory separately while iterating through the
   instruction list and executing each instruction).

 drivers/staging/comedi/comedi_fops.c | 48 ++++++++++++++++++----------
 1 file changed, 31 insertions(+), 17 deletions(-)

diff --git a/drivers/staging/comedi/comedi_fops.c 
b/drivers/staging/comedi/comedi_fops.c
index ceb6ba5dd57c..5d2fcbfe02af 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -1501,25 +1501,21 @@ static int parse_insn(struct comedi_device *dev, struct 
comedi_insn *insn,
  *     data (for reads) to insns[].data pointers
  */
 /* arbitrary limits */
-#define MAX_SAMPLES 256
+#define MIN_SAMPLES 16
+#define MAX_SAMPLES 65536
 static int do_insnlist_ioctl(struct comedi_device *dev,
                             struct comedi_insnlist __user *arg, void *file)
 {
        struct comedi_insnlist insnlist;
        struct comedi_insn *insns = NULL;
        unsigned int *data = NULL;
+       unsigned int max_n_data_required = MIN_SAMPLES;
        int i = 0;
        int ret = 0;
 
        if (copy_from_user(&insnlist, arg, sizeof(insnlist)))
                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);
        if (!insns) {
                ret = -ENOMEM;
@@ -1533,13 +1529,26 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
                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) {
                        dev_dbg(dev->class_dev,
                                "number of samples too large\n");
                        ret = -EINVAL;
                        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 (copy_from_user(data, insns[i].data,
                                           insns[i].n * sizeof(unsigned int))) {
@@ -1593,22 +1602,27 @@ static int do_insn_ioctl(struct comedi_device *dev,
 {
        struct comedi_insn insn;
        unsigned int *data = NULL;
+       unsigned int n_data = MIN_SAMPLES;
        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))) {
-               ret = -EFAULT;
-               goto error;
+               return -EFAULT;
        }
 
+       n_data = max(n_data, insn.n);
+
        /* This is where the behavior of insn and insnlist deviate. */
-       if (insn.n > MAX_SAMPLES)
+       if (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 (copy_from_user(data,
                                   insn.data,
-- 
2.17.1

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to