Commit 1f5cc359 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman
Browse files

staging: comedi: kcomedilib: fix a __user space access issue



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: 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 94174847
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -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];