[PATCH 26/44] drivers/scsi: use min() instead of min_t()

david.laight.linux@gmail.com posted 44 patches 1 week, 5 days ago
There is a newer version of this series
[PATCH 26/44] drivers/scsi: use min() instead of min_t()
Posted by david.laight.linux@gmail.com 1 week, 5 days ago
From: David Laight <david.laight.linux@gmail.com>

min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'.
Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long'
and so cannot discard significant bits.

In this case the 'unsigned long' value is small enough that the result
is ok.

Detected by an extra check added to min_t().

Signed-off-by: David Laight <david.laight.linux@gmail.com>
---
 drivers/scsi/hosts.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 17173239301e..b15896560cf6 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -247,7 +247,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
 	shost->dma_dev = dma_dev;
 
 	if (dma_dev->dma_mask) {
-		shost->max_sectors = min_t(unsigned int, shost->max_sectors,
+		shost->max_sectors = min(shost->max_sectors,
 				dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT);
 	}
 
-- 
2.39.5
Re: [PATCH 26/44] drivers/scsi: use min() instead of min_t()
Posted by Bart Van Assche 1 week, 5 days ago
On 11/19/25 2:41 PM, david.laight.linux@gmail.com wrote:
> From: David Laight <david.laight.linux@gmail.com>
> 
> min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'.
> Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long'
> and so cannot discard significant bits.
> 
> In this case the 'unsigned long' value is small enough that the result
> is ok.
> 
> Detected by an extra check added to min_t().
> 
> Signed-off-by: David Laight <david.laight.linux@gmail.com>
> ---
>   drivers/scsi/hosts.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
> index 17173239301e..b15896560cf6 100644
> --- a/drivers/scsi/hosts.c
> +++ b/drivers/scsi/hosts.c
> @@ -247,7 +247,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
>   	shost->dma_dev = dma_dev;
>   
>   	if (dma_dev->dma_mask) {
> -		shost->max_sectors = min_t(unsigned int, shost->max_sectors,
> +		shost->max_sectors = min(shost->max_sectors,
>   				dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT);
>   	}

So instead of the type cast performed by min_t() potentially discarding
bits, the assignment potentially discards bits. I'm not sure this is an
improvement.

Thanks,

Bart.
Re: [PATCH 26/44] drivers/scsi: use min() instead of min_t()
Posted by David Laight 1 week, 2 days ago
On Wed, 19 Nov 2025 15:09:02 -0800
Bart Van Assche <bvanassche@acm.org> wrote:

> On 11/19/25 2:41 PM, david.laight.linux@gmail.com wrote:
> > From: David Laight <david.laight.linux@gmail.com>
> > 
> > min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'.
> > Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long'
> > and so cannot discard significant bits.
> > 
> > In this case the 'unsigned long' value is small enough that the result
> > is ok.
> > 
> > Detected by an extra check added to min_t().
> > 
> > Signed-off-by: David Laight <david.laight.linux@gmail.com>
> > ---
> >   drivers/scsi/hosts.c | 2 +-
> >   1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
> > index 17173239301e..b15896560cf6 100644
> > --- a/drivers/scsi/hosts.c
> > +++ b/drivers/scsi/hosts.c
> > @@ -247,7 +247,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
> >   	shost->dma_dev = dma_dev;
> >   
> >   	if (dma_dev->dma_mask) {
> > -		shost->max_sectors = min_t(unsigned int, shost->max_sectors,
> > +		shost->max_sectors = min(shost->max_sectors,
> >   				dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT);
> >   	}  
> 
> So instead of the type cast performed by min_t() potentially discarding
> bits, the assignment potentially discards bits. I'm not sure this is an
> improvement.

In this case the assignment is fine - shost->max_sectors is on both sides,
so the value can only go down.

The issue is that dma_max_mapping_size() returns a 64bit value.
So if you have some magic high speed interface with a 2TB 'dma map'
then 'dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT' is '1u << 32' so
is zero when cast to 'unsigned int'.
At that point things start going horribly wrong.

I don't think there are any such interfaces - so the bug this fixes
can't happen.

OTOH the same test does pick up a lot (and I mean a lot [1]) of driver code
that contains code like:
ssize_t do_xxx(...  size_t len)
{
	unsigned int copied = 0, frag_len;
	while (copied < len) {
		frag_len = min_t(unsigned int, len, MAX_FRAG);
		....
		copied += frag_len;
		len -= frag_len;
	}
	return copied;
}

If you manage to request a transfer for 4G (or more) then it doesn't work.
Now there might be a test earlier that stops that happening in a lot of places.
But from the perspective of the function it isn't true.
(I suspect readv() with a single iov[] can generate a big buffer.)
The compile-time test detects that (unsigned int)len may not equal len
and it can be fixed by changing to min(len, MAX_FRAG);
In this case it might be a real bug.

Note that a read can be truncated after a few bytes - it is only the
buffer size that needs to be massive.

	David

[1] I've just built allmodconfig - 'only' 488 more files needed changing.
A fair number of 'real bugs', a few false positives because PAGE_SIZE and
sizeof() are 64bit, and a lot of dubious code.
By far the worst are all the min_t([u8|u16], ...) in many cases the code
uses the type of the destination - so you get (eg):
	u8_var = min(u8, value_32 / 2, 255);

> 
> Thanks,
> 
> Bart.
Re: [PATCH 26/44] drivers/scsi: use min() instead of min_t()
Posted by David Laight 1 week, 4 days ago
On Wed, 19 Nov 2025 15:09:02 -0800
Bart Van Assche <bvanassche@acm.org> wrote:

> On 11/19/25 2:41 PM, david.laight.linux@gmail.com wrote:
> > From: David Laight <david.laight.linux@gmail.com>
> > 
> > min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'.
> > Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long'
> > and so cannot discard significant bits.
> > 
> > In this case the 'unsigned long' value is small enough that the result
> > is ok.
> > 
> > Detected by an extra check added to min_t().
> > 
> > Signed-off-by: David Laight <david.laight.linux@gmail.com>
> > ---
> >   drivers/scsi/hosts.c | 2 +-
> >   1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
> > index 17173239301e..b15896560cf6 100644
> > --- a/drivers/scsi/hosts.c
> > +++ b/drivers/scsi/hosts.c
> > @@ -247,7 +247,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
> >   	shost->dma_dev = dma_dev;
> >   
> >   	if (dma_dev->dma_mask) {
> > -		shost->max_sectors = min_t(unsigned int, shost->max_sectors,
> > +		shost->max_sectors = min(shost->max_sectors,
> >   				dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT);
> >   	}  
> 
> So instead of the type cast performed by min_t() potentially discarding
> bits, the assignment potentially discards bits. I'm not sure this is an
> improvement.

The assignment cant discard bits because shost->max_sectors is also on the RHS.
In any case you'd need a 2TB mapping size to get an error.
That probably cant happen for other reasons.

The patch is remove a false positive for a check added to min_t(T, a, b)
that sizeof (b) <= sizeof (T).

	David

> 
> Thanks,
> 
> Bart.
>