diff mbox series

iio: buffer: Fix demux update

Message ID 20201112144323.28887-1-nuno.sa@analog.com (mailing list archive)
State New, archived
Headers show
Series iio: buffer: Fix demux update | expand

Commit Message

Nuno Sa Nov. 12, 2020, 2:43 p.m. UTC
When updating the buffer demux, we will skip a scan element from the
device in the case `in_ind != out_ind` and we enter the while loop.
in_ind should only be refreshed with `find_next_bit()` in the end of the
loop.

Fixes: 5ada4ea9be16 ("staging:iio: add demux optionally to path from device to buffer")
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
 drivers/iio/industrialio-buffer.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

Comments

Jonathan Cameron Nov. 12, 2020, 5:28 p.m. UTC | #1
On Thu, 12 Nov 2020 15:43:22 +0100
Nuno Sá <nuno.sa@analog.com> wrote:

> When updating the buffer demux, we will skip a scan element from the
> device in the case `in_ind != out_ind` and we enter the while loop.
> in_ind should only be refreshed with `find_next_bit()` in the end of the
> loop.
> 
> Fixes: 5ada4ea9be16 ("staging:iio: add demux optionally to path from device to buffer")
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>

Yikes that's been there a long time.

Could you provide an example of a particular layout and the result of this being wrong?

Thanks,

Jonathan

> ---
>  drivers/iio/industrialio-buffer.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index a4f6bb96d4f4..276b609d7917 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -865,12 +865,12 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev,
>  				       indio_dev->masklength,
>  				       in_ind + 1);
>  		while (in_ind != out_ind) {
> -			in_ind = find_next_bit(indio_dev->active_scan_mask,
> -					       indio_dev->masklength,
> -					       in_ind + 1);
>  			length = iio_storage_bytes_for_si(indio_dev, in_ind);
>  			/* Make sure we are aligned */
>  			in_loc = roundup(in_loc, length) + length;
> +			in_ind = find_next_bit(indio_dev->active_scan_mask,
> +					       indio_dev->masklength,
> +					       in_ind + 1);
>  		}
>  		length = iio_storage_bytes_for_si(indio_dev, in_ind);
>  		out_loc = roundup(out_loc, length);
Nuno Sa Nov. 13, 2020, 7:55 a.m. UTC | #2
> -----Original Message-----
> From: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
> Sent: Thursday, November 12, 2020 6:28 PM
> To: Sa, Nuno <Nuno.Sa@analog.com>
> Cc: linux-iio@vger.kernel.org; Jonathan Cameron <jic23@kernel.org>;
> Lars-Peter Clausen <lars@metafoo.de>; Peter Meerwald-Stadler
> <pmeerw@pmeerw.net>
> Subject: Re: [PATCH] iio: buffer: Fix demux update
> 
> 
> On Thu, 12 Nov 2020 15:43:22 +0100
> Nuno Sá <nuno.sa@analog.com> wrote:
> 
> > When updating the buffer demux, we will skip a scan element from
> the
> > device in the case `in_ind != out_ind` and we enter the while loop.
> > in_ind should only be refreshed with `find_next_bit()` in the end of
> the
> > loop.
> >
> > Fixes: 5ada4ea9be16 ("staging:iio: add demux optionally to path from
> device to buffer")
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> 
> Yikes that's been there a long time.
> 
> Could you provide an example of a particular layout and the result of
> this being wrong?
> 

Hi Jonathan,

Let's say:

iio_dev_mask: 0x0111
buffer_mask: 0x0100

We would get out_ind = 2 and in_ind = 0 and  enter the loop. In the first
iteration we call find_next_bit() before doing the in_ind=0 computation which means we 
will skip it and go directly to bit 1... And if we continue the path flow, we see that bit 2 will
be computed two times, so if we are lucky and scan_index0_len == scan_index2_len this
will go unnoticed...

Honestly, I didn't test this but it looks one of those things more or less clear by reading
the code or am I missing something here?

- Nuno Sá

> Thanks,
> 
> Jonathan
>
Jonathan Cameron Nov. 14, 2020, 4:26 p.m. UTC | #3
On Fri, 13 Nov 2020 07:55:21 +0000
"Sa, Nuno" <Nuno.Sa@analog.com> wrote:

> > -----Original Message-----
> > From: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
> > Sent: Thursday, November 12, 2020 6:28 PM
> > To: Sa, Nuno <Nuno.Sa@analog.com>
> > Cc: linux-iio@vger.kernel.org; Jonathan Cameron <jic23@kernel.org>;
> > Lars-Peter Clausen <lars@metafoo.de>; Peter Meerwald-Stadler
> > <pmeerw@pmeerw.net>
> > Subject: Re: [PATCH] iio: buffer: Fix demux update
> > 
> > 
> > On Thu, 12 Nov 2020 15:43:22 +0100
> > Nuno Sá <nuno.sa@analog.com> wrote:
> >   
> > > When updating the buffer demux, we will skip a scan element from  
> > the  
> > > device in the case `in_ind != out_ind` and we enter the while loop.
> > > in_ind should only be refreshed with `find_next_bit()` in the end of  
> > the  
> > > loop.
> > >
> > > Fixes: 5ada4ea9be16 ("staging:iio: add demux optionally to path from  
> > device to buffer")  
> > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>  
> > 
> > Yikes that's been there a long time.
> > 
> > Could you provide an example of a particular layout and the result of
> > this being wrong?
> >   
> 
> Hi Jonathan,
> 
> Let's say:
> 
> iio_dev_mask: 0x0111
> buffer_mask: 0x0100
> 
> We would get out_ind = 2 and in_ind = 0 and  enter the loop. In the first
> iteration we call find_next_bit() before doing the in_ind=0 computation which means we 
> will skip it and go directly to bit 1... And if we continue the path flow, we see that bit 2 will
> be computed two times, so if we are lucky and scan_index0_len == scan_index2_len this
> will go unnoticed...
> 
> Honestly, I didn't test this but it looks one of those things more or less clear by reading
> the code or am I missing something here?

Mostly I was wondering why it hadn't bitten us before.  I think you've identified
why with your "if we are lucky and scan_index0_len == scan_index2_len" then this will
go unnoticed.   

It's very rare (though not unheard of) for a device to have it's main channels
of different widths (timestamp doesn't matter for this as it is always at the
end).  The demux also only kicks in if we have a restricted channel
mask (or are using a kfifo and a buffer_cb which is rather rare).  I suspect
we have few if any devices that actually run into this problem.

I guess I originally tested this code with devices I had at the time and none of
them would have tripped this.

Anyhow, whilst I agree with your analysis I'd like to leave this on list for
perhaps another week before applying it on the basis I'm paranoid and would
ideally like a few more eyes on this.

Good spot!

Jonathan

> 
> - Nuno Sá
> 
> > Thanks,
> > 
> > Jonathan
> >
Jonathan Cameron Nov. 28, 2020, 3:50 p.m. UTC | #4
On Sat, 14 Nov 2020 16:26:38 +0000
Jonathan Cameron <jic23@kernel.org> wrote:

> On Fri, 13 Nov 2020 07:55:21 +0000
> "Sa, Nuno" <Nuno.Sa@analog.com> wrote:
> 
> > > -----Original Message-----
> > > From: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
> > > Sent: Thursday, November 12, 2020 6:28 PM
> > > To: Sa, Nuno <Nuno.Sa@analog.com>
> > > Cc: linux-iio@vger.kernel.org; Jonathan Cameron <jic23@kernel.org>;
> > > Lars-Peter Clausen <lars@metafoo.de>; Peter Meerwald-Stadler
> > > <pmeerw@pmeerw.net>
> > > Subject: Re: [PATCH] iio: buffer: Fix demux update
> > > 
> > > 
> > > On Thu, 12 Nov 2020 15:43:22 +0100
> > > Nuno Sá <nuno.sa@analog.com> wrote:
> > >     
> > > > When updating the buffer demux, we will skip a scan element from    
> > > the    
> > > > device in the case `in_ind != out_ind` and we enter the while loop.
> > > > in_ind should only be refreshed with `find_next_bit()` in the end of    
> > > the    
> > > > loop.
> > > >
> > > > Fixes: 5ada4ea9be16 ("staging:iio: add demux optionally to path from    
> > > device to buffer")    
> > > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>    
> > > 
> > > Yikes that's been there a long time.
> > > 
> > > Could you provide an example of a particular layout and the result of
> > > this being wrong?
> > >     
> > 
> > Hi Jonathan,
> > 
> > Let's say:
> > 
> > iio_dev_mask: 0x0111
> > buffer_mask: 0x0100
> > 
> > We would get out_ind = 2 and in_ind = 0 and  enter the loop. In the first
> > iteration we call find_next_bit() before doing the in_ind=0 computation which means we 
> > will skip it and go directly to bit 1... And if we continue the path flow, we see that bit 2 will
> > be computed two times, so if we are lucky and scan_index0_len == scan_index2_len this
> > will go unnoticed...
> > 
> > Honestly, I didn't test this but it looks one of those things more or less clear by reading
> > the code or am I missing something here?  
> 
> Mostly I was wondering why it hadn't bitten us before.  I think you've identified
> why with your "if we are lucky and scan_index0_len == scan_index2_len" then this will
> go unnoticed.   
> 
> It's very rare (though not unheard of) for a device to have it's main channels
> of different widths (timestamp doesn't matter for this as it is always at the
> end).  The demux also only kicks in if we have a restricted channel
> mask (or are using a kfifo and a buffer_cb which is rather rare).  I suspect
> we have few if any devices that actually run into this problem.
> 
> I guess I originally tested this code with devices I had at the time and none of
> them would have tripped this.
> 
> Anyhow, whilst I agree with your analysis I'd like to leave this on list for
> perhaps another week before applying it on the basis I'm paranoid and would
> ideally like a few more eyes on this.
> 
Ah well. I'll take silence as meaning either everyone is happy or no
one else is going to read it ;)

Applied with a bit more text in the description to highlight that,
whilst a bug, it's actually not a common situation.

Given timing I've applied this to the togreg branch of iio.git
ready for the next merge window.

thanks,

Jonathan

> Good spot!
> 
> Jonathan
> 
> > 
> > - Nuno Sá
> >   
> > > Thanks,
> > > 
> > > Jonathan
> > >     
>
diff mbox series

Patch

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index a4f6bb96d4f4..276b609d7917 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -865,12 +865,12 @@  static int iio_buffer_update_demux(struct iio_dev *indio_dev,
 				       indio_dev->masklength,
 				       in_ind + 1);
 		while (in_ind != out_ind) {
-			in_ind = find_next_bit(indio_dev->active_scan_mask,
-					       indio_dev->masklength,
-					       in_ind + 1);
 			length = iio_storage_bytes_for_si(indio_dev, in_ind);
 			/* Make sure we are aligned */
 			in_loc = roundup(in_loc, length) + length;
+			in_ind = find_next_bit(indio_dev->active_scan_mask,
+					       indio_dev->masklength,
+					       in_ind + 1);
 		}
 		length = iio_storage_bytes_for_si(indio_dev, in_ind);
 		out_loc = roundup(out_loc, length);