diff mbox series

[2/2] xarray: Fix race in xas_find_chunk()

Message ID 20231011150252.32737-2-jack@suse.cz (mailing list archive)
State New, archived
Headers show
Series lib/find: Fix KCSAN warnings in find_*_bit() functions | expand

Commit Message

Jan Kara Oct. 11, 2023, 3:02 p.m. UTC
xas_find_chunk() can be called only under RCU protection and thus tags
can be changing while it is working. Hence the code:

	unsigned long data = *addr & (~0UL << offset);
	if (data)
		return __ffs(data);

is prone to 'data' being refetched from addr by the compiler and thus
calling __ffs() with 0 argument and undefined results.

Fix the problem by removing XA_CHUNK_SIZE == BITS_PER_LONG special case
completely. find_next_bit() gets this right and there's no performance
difference because it is inline and has the very same special branch for
const-sized bitmaps anyway.

Reported-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>
CC: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/linux/xarray.h | 9 ---------
 1 file changed, 9 deletions(-)

Comments

Matthew Wilcox Oct. 11, 2023, 3:38 p.m. UTC | #1
On Wed, Oct 11, 2023 at 05:02:32PM +0200, Jan Kara wrote:
> xas_find_chunk() can be called only under RCU protection and thus tags
> can be changing while it is working. Hence the code:
> 
> 	unsigned long data = *addr & (~0UL << offset);
> 	if (data)
> 		return __ffs(data);
> 
> is prone to 'data' being refetched from addr by the compiler and thus
> calling __ffs() with 0 argument and undefined results.
> 
> Fix the problem by removing XA_CHUNK_SIZE == BITS_PER_LONG special case
> completely. find_next_bit() gets this right and there's no performance
> difference because it is inline and has the very same special branch for
> const-sized bitmaps anyway.
> 
> Reported-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>
> CC: Matthew Wilcox <willy@infradead.org>
> Signed-off-by: Jan Kara <jack@suse.cz>

Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Mirsad Todorovac Oct. 11, 2023, 8:40 p.m. UTC | #2
On 10/11/23 17:02, Jan Kara wrote:
> xas_find_chunk() can be called only under RCU protection and thus tags
> can be changing while it is working. Hence the code:
> 
> 	unsigned long data = *addr & (~0UL << offset);
> 	if (data)
> 		return __ffs(data);
> 
> is prone to 'data' being refetched from addr by the compiler and thus
> calling __ffs() with 0 argument and undefined results.
> 
> Fix the problem by removing XA_CHUNK_SIZE == BITS_PER_LONG special case
> completely. find_next_bit() gets this right and there's no performance
> difference because it is inline and has the very same special branch for
> const-sized bitmaps anyway.
> 
> Reported-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>
> CC: Matthew Wilcox <willy@infradead.org>
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>   include/linux/xarray.h | 9 ---------
>   1 file changed, 9 deletions(-)
> 
> diff --git a/include/linux/xarray.h b/include/linux/xarray.h
> index cb571dfcf4b1..07700a2c8870 100644
> --- a/include/linux/xarray.h
> +++ b/include/linux/xarray.h
> @@ -1718,15 +1718,6 @@ static inline unsigned int xas_find_chunk(struct xa_state *xas, bool advance,
>   
>   	if (advance)
>   		offset++;
> -	if (XA_CHUNK_SIZE == BITS_PER_LONG) {
> -		if (offset < XA_CHUNK_SIZE) {
> -			unsigned long data = *addr & (~0UL << offset);
> -			if (data)
> -				return __ffs(data);
> -		}
> -		return XA_CHUNK_SIZE;
> -	}
> -
>   	return find_next_bit(addr, XA_CHUNK_SIZE, offset);
>   }
>   

Tested-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>

Best regards,
Mirsad
diff mbox series

Patch

diff --git a/include/linux/xarray.h b/include/linux/xarray.h
index cb571dfcf4b1..07700a2c8870 100644
--- a/include/linux/xarray.h
+++ b/include/linux/xarray.h
@@ -1718,15 +1718,6 @@  static inline unsigned int xas_find_chunk(struct xa_state *xas, bool advance,
 
 	if (advance)
 		offset++;
-	if (XA_CHUNK_SIZE == BITS_PER_LONG) {
-		if (offset < XA_CHUNK_SIZE) {
-			unsigned long data = *addr & (~0UL << offset);
-			if (data)
-				return __ffs(data);
-		}
-		return XA_CHUNK_SIZE;
-	}
-
 	return find_next_bit(addr, XA_CHUNK_SIZE, offset);
 }