diff mbox series

[v3,10/27] iio:light:rpr0521 Fix timestamp alignment and prevent data leak.

Message ID 20200722155103.979802-11-jic23@kernel.org (mailing list archive)
State New, archived
Headers show
Series IIO: Fused set 1 and 2 of timestamp alignment fixes | expand

Commit Message

Jonathan Cameron July 22, 2020, 3:50 p.m. UTC
From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

One of a class of bugs pointed out by Lars in a recent review.
iio_push_to_buffers_with_timestamp assumes the buffer used is aligned
to the size of the timestamp (8 bytes).  This is not guaranteed in
this driver which uses an array of smaller elements on the stack.
As Lars also noted this anti pattern can involve a leak of data to
userspace and that indeed can happen here.  We close both issues by
moving to a suitable structure in the iio_priv().
This data is allocated with kzalloc so no data can leak appart
from previous readings and in this case the status byte from the device.

The forced alignment of ts is not necessary in this case but it
potentially makes the code less fragile.

Fixes: e12ffd241c00 ("iio: light: rpr0521 triggered buffer")
Cc: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/iio/light/rpr0521.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

Comments

Andy Shevchenko July 22, 2020, 7:47 p.m. UTC | #1
On Wed, Jul 22, 2020 at 6:53 PM Jonathan Cameron <jic23@kernel.org> wrote:
>
> From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>
> One of a class of bugs pointed out by Lars in a recent review.
> iio_push_to_buffers_with_timestamp assumes the buffer used is aligned
> to the size of the timestamp (8 bytes).  This is not guaranteed in
> this driver which uses an array of smaller elements on the stack.
> As Lars also noted this anti pattern can involve a leak of data to
> userspace and that indeed can happen here.  We close both issues by
> moving to a suitable structure in the iio_priv().

> This data is allocated with kzalloc so no data can leak appart

apart

> from previous readings and in this case the status byte from the device.
>
> The forced alignment of ts is not necessary in this case but it
> potentially makes the code less fragile.

...

> +        * Note that the read will put garbage data into
> +        * the padding but this should not be a problem

> +               u8 garbage;

>         err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,
> -               &buffer,
> +               data->scan.channels,
>                 (3 * 2) + 1);   /* 3 * 16-bit + (discarded) int clear reg. */

But can't we read the interrupt clear register separately?
Jonathan Cameron July 23, 2020, 11:29 a.m. UTC | #2
On Wed, 22 Jul 2020 22:47:46 +0300
Andy Shevchenko <andy.shevchenko@gmail.com> wrote:

> On Wed, Jul 22, 2020 at 6:53 PM Jonathan Cameron <jic23@kernel.org> wrote:
> >
> > From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> >
> > One of a class of bugs pointed out by Lars in a recent review.
> > iio_push_to_buffers_with_timestamp assumes the buffer used is aligned
> > to the size of the timestamp (8 bytes).  This is not guaranteed in
> > this driver which uses an array of smaller elements on the stack.
> > As Lars also noted this anti pattern can involve a leak of data to
> > userspace and that indeed can happen here.  We close both issues by
> > moving to a suitable structure in the iio_priv().  
> 
> > This data is allocated with kzalloc so no data can leak appart  
> 
> apart
> 
> > from previous readings and in this case the status byte from the device.
> >
> > The forced alignment of ts is not necessary in this case but it
> > potentially makes the code less fragile.  
> 
> ...
> 
> > +        * Note that the read will put garbage data into
> > +        * the padding but this should not be a problem  
> 
> > +               u8 garbage;  
> 
> >         err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,
> > -               &buffer,
> > +               data->scan.channels,
> >                 (3 * 2) + 1);   /* 3 * 16-bit + (discarded) int clear reg. */  
> 
> But can't we read the interrupt clear register separately?
> 

Potentially though I have no idea if there is an odd quirk there. I'm not
immediately seeing anything on the datasheet about it.
Would need a tested-by from someone with hardware to confirm it is fine to
do it as two reads though.

Would be nice to get rid of the non standard handling so well worth
pursuing in v4 of this set.

Jonathan
Jonathan Cameron Sept. 19, 2020, 4:31 p.m. UTC | #3
On Thu, 23 Jul 2020 12:29:39 +0100
Jonathan Cameron <Jonathan.Cameron@Huawei.com> wrote:

> On Wed, 22 Jul 2020 22:47:46 +0300
> Andy Shevchenko <andy.shevchenko@gmail.com> wrote:
> 
> > On Wed, Jul 22, 2020 at 6:53 PM Jonathan Cameron <jic23@kernel.org> wrote:  
> > >
> > > From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> > >
> > > One of a class of bugs pointed out by Lars in a recent review.
> > > iio_push_to_buffers_with_timestamp assumes the buffer used is aligned
> > > to the size of the timestamp (8 bytes).  This is not guaranteed in
> > > this driver which uses an array of smaller elements on the stack.
> > > As Lars also noted this anti pattern can involve a leak of data to
> > > userspace and that indeed can happen here.  We close both issues by
> > > moving to a suitable structure in the iio_priv().    
> >   
> > > This data is allocated with kzalloc so no data can leak appart    
> > 
> > apart
> >   
> > > from previous readings and in this case the status byte from the device.
> > >
> > > The forced alignment of ts is not necessary in this case but it
> > > potentially makes the code less fragile.    
> > 
> > ...
> >   
> > > +        * Note that the read will put garbage data into
> > > +        * the padding but this should not be a problem    
> >   
> > > +               u8 garbage;    
> >   
> > >         err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,
> > > -               &buffer,
> > > +               data->scan.channels,
> > >                 (3 * 2) + 1);   /* 3 * 16-bit + (discarded) int clear reg. */    
> > 
> > But can't we read the interrupt clear register separately?
> >   
> 
> Potentially though I have no idea if there is an odd quirk there. I'm not
> immediately seeing anything on the datasheet about it.
> Would need a tested-by from someone with hardware to confirm it is fine to
> do it as two reads though.
> 
> Would be nice to get rid of the non standard handling so well worth
> pursuing in v4 of this set.

Mikko, came back to me off list on this (was stuck with an evil email client)
The cost would be significant of doing an extra read.  20 I2C clock cycles.
As such, I'd rather keep the unusual handling in here.

Will fix the typo and carry this one forwards to v4.

thanks,

Jonathan

> 
> Jonathan
> 
>
diff mbox series

Patch

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index aa2972b04833..31224a33bade 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -194,6 +194,17 @@  struct rpr0521_data {
 	bool pxs_need_dis;
 
 	struct regmap *regmap;
+
+	/*
+	 * Ensure correct naturally aligned timestamp.
+	 * Note that the read will put garbage data into
+	 * the padding but this should not be a problem
+	 */
+	struct {
+		__le16 channels[3];
+		u8 garbage;
+		s64 ts __aligned(8);
+	} scan;
 };
 
 static IIO_CONST_ATTR(in_intensity_scale_available, RPR0521_ALS_SCALE_AVAIL);
@@ -449,8 +460,6 @@  static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p)
 	struct rpr0521_data *data = iio_priv(indio_dev);
 	int err;
 
-	u8 buffer[16]; /* 3 16-bit channels + padding + ts */
-
 	/* Use irq timestamp when reasonable. */
 	if (iio_trigger_using_own(indio_dev) && data->irq_timestamp) {
 		pf->timestamp = data->irq_timestamp;
@@ -461,11 +470,11 @@  static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p)
 		pf->timestamp = iio_get_time_ns(indio_dev);
 
 	err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,
-		&buffer,
+		data->scan.channels,
 		(3 * 2) + 1);	/* 3 * 16-bit + (discarded) int clear reg. */
 	if (!err)
 		iio_push_to_buffers_with_timestamp(indio_dev,
-						   buffer, pf->timestamp);
+						   &data->scan, pf->timestamp);
 	else
 		dev_err(&data->client->dev,
 			"Trigger consumer can't read from sensor.\n");