staging: comedi: kcomedilib: fix a __user space access issue
authorH Hartley Sweeten <hartleys@visionengravers.com>
Thu, 20 Sep 2012 00:26:53 +0000 (17:26 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Sep 2012 16:26:20 +0000 (09:26 -0700)
The 'data' field in struct comedi_insn is an unsigned int __user *.
The comedi core copies this data to kernel space before passing it
on to a drivers insn_bits/insn_config method.

kcomedilib provides an interface for external kernel modules to
use the comedi drivers. This interface creates a comedi_insn
that is then passed to the comedi drivers insn_bits/insn_config
method. Unfortunately, kcomedilib is using the comedi_insn 'data'
field directly which results in some sparse warnings:

  warning: incorrect type in argument 4 (different address spaces)
     expected unsigned int *<noident>
     got unsigned int [noderef] <asn:1>*data

  warning: incorrect type in assignment (different address spaces)
     expected unsigned int [noderef] <asn:1>*[addressable] [assigned] data
     got unsigned int *<noident>

Fix this by passing the kernel data directly, as a separate parameter,
instead of trying to put in into the comedi_insn 'data' field. This is
how the comedi core handles the data from user space.

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>
drivers/staging/comedi/kcomedilib/kcomedilib_main.c

index f96416d1d2f7521d1f07814cc3b018aee1bb1ac8..3f20ea55b8d0b45452bba1ed2dc4350cd6be007a 100644 (file)
@@ -80,7 +80,9 @@ int comedi_close(struct comedi_device *d)
 }
 EXPORT_SYMBOL(comedi_close);
 
-static int comedi_do_insn(struct comedi_device *dev, struct comedi_insn *insn)
+static int comedi_do_insn(struct comedi_device *dev,
+                         struct comedi_insn *insn,
+                         unsigned int *data)
 {
        struct comedi_subdevice *s;
        int ret = 0;
@@ -115,11 +117,11 @@ static int comedi_do_insn(struct comedi_device *dev, struct comedi_insn *insn)
 
        switch (insn->insn) {
        case INSN_BITS:
-               ret = s->insn_bits(dev, s, insn, insn->data);
+               ret = s->insn_bits(dev, s, insn, data);
                break;
        case INSN_CONFIG:
                /* XXX should check instruction length */
-               ret = s->insn_config(dev, s, insn, insn->data);
+               ret = s->insn_config(dev, s, insn, data);
                break;
        default:
                ret = -EINVAL;
@@ -140,11 +142,10 @@ int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,
        memset(&insn, 0, sizeof(insn));
        insn.insn = INSN_CONFIG;
        insn.n = 1;
-       insn.data = &io;
        insn.subdev = subdev;
        insn.chanspec = CR_PACK(chan, 0, 0);
 
-       return comedi_do_insn(dev, &insn);
+       return comedi_do_insn(dev, &insn, &io);
 }
 EXPORT_SYMBOL(comedi_dio_config);
 
@@ -158,13 +159,12 @@ int comedi_dio_bitfield(struct comedi_device *dev, unsigned int subdev,
        memset(&insn, 0, sizeof(insn));
        insn.insn = INSN_BITS;
        insn.n = 2;
-       insn.data = data;
        insn.subdev = subdev;
 
        data[0] = mask;
        data[1] = *bits;
 
-       ret = comedi_do_insn(dev, &insn);
+       ret = comedi_do_insn(dev, &insn, data);
 
        *bits = data[1];