RE: [PATCH 0/1] Slightly relax the type checking done by min() and max().

David Laight posted 1 patch 2 years, 9 months ago
Only 0 patches received!
There is a newer version of this series
RE: [PATCH 0/1] Slightly relax the type checking done by min() and max().
Posted by David Laight 2 years, 9 months ago
From: 'Andy Shevchenko'
> Sent: 25 November 2022 17:48
> 
> On Fri, Nov 25, 2022 at 04:14:58PM +0000, David Laight wrote:
> > From: 'Andy Shevchenko'
> > > Sent: 25 November 2022 15:58
> > > On Fri, Nov 25, 2022 at 03:27:07PM +0000, David Laight wrote:
> 
> ...
> 
> > > Any better example, please?
> >
> > How about:
> 
> Better, indeed.
> 
> > data_size = min_t(u16, buf_size, len);
> >
> > https://elixir.bootlin.com/linux/v6.1-rc6/source/kernel/printk/printk_ringbuffer.c#L1738
> >
> > Now, maybe, you could claim that buf_size > 64k never happens.
> > But the correct cast here is u32 to match buf_size.
> > len (being u16) will be promoted to int before the compare.
> >
> > Just search the kernel for "min_t(u8," or "min_t(u16," while some might
> > be ok, I really wouldn't want to verify each case.
> >
> > If you look hard enough there are also some:
> > 	u32_var = min_t(u32, u32_val, u64_val);
> > where the intent is to limit values that might be invalid for u32.
> 
> Wouldn't be better to actually issue a warning if the desired type is shorter
> than one of the min_t() arguments?
> 
> Then you go through all cases and fix them accordingly.

That is an option, but that is separate from this change.

> Blindly relaxing the rules is not an option in my opinion.

The point is I'm not really relaxing the rules.
If anything I'm actually tightening them by allowing min() to
be used in more cases - so letting the buggy min_t() casts be
removed at some point in the future.

The purpose of the type check is to error code like:
	int x = -4;
	x = min(x, 100u);
where integer promotion changes the comparison 'x < 100u' to
the unexpected '(unsigned int)x < 100u' so x is set to 100.

However is also errors the opposite:
	unsigned int x = 4;
	x = min(x, 100);
But, in this case, the compiler just converts 100 to 100u and
everything is fine.

It also errors the perfectly valid (and reasonable looking):
	unsigned char x = 4;
	x = min(x, 100u);
because 100u is 'unsigned int'.
Indeed, 'x' gets converted to 'signed int' for the comparison.

When the (usually) RHS is a compile-time constant that is known
to be positive (and less than 2^31) I'm just changing the test to:
	if (variable < (int)constant)
If 'variable' is a signed type this always generates the
required signed compare.
If 'variable is 'unsigned char' or 'unsigned short' then it
gets promoted to signed int and a signed compare of positive
values is done.
If variable is 'unsigned int', 'unsigned long' or 'unsigned long long'
then the RHS is converted to unsigned - but keeps its value since
it is known to be positive.
In all cases the outcome is exactly what is required.
No change of putting in the wrong cast.

There are a few other common cases that are valid but currently
generate an error:
	min(s8_var, int_expr)
	min(u8_var, int_expr)
	min(u8_var, unsigned_int_expr)
These generate an error regardless of whether the sizes match:
	min(int_expr, long_expr)
	min(unsigned_int_expr, unsigned_long_expr)
	min(unsigned_long_expr, unsigned_long_long_expr)
This is less common but should also be allowed:
	min(long_long_int_expr, unsigned_int_expr)
(ie when the signed type is larger than the unsigned one)

I have a plan for how to allow all those as well.

Checking typeof((x) + 0) against typeof((y) + 0) allows for the
promotion of char/short to signed int - but not any of the
other comparisons that never implicitly convert a signed value
to unsigned (and hence negative values to large positive ones).

	David

	

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Re: [PATCH 0/1] Slightly relax the type checking done by min() and max().
Posted by Andrew Morton 2 years, 9 months ago
On Fri, 25 Nov 2022 19:47:14 +0000 David Laight <David.Laight@ACULAB.COM> wrote:

> > Blindly relaxing the rules is not an option in my opinion.
> 
> The point is I'm not really relaxing the rules.
> If anything I'm actually tightening them by allowing min() to
> be used in more cases - so letting the buggy min_t() casts be
> removed at some point in the future.

That sounds very virtuous.

It would be helpful to see a few "convert min_t to min" patches
to see this proposal in action.
RE: [PATCH 0/1] Slightly relax the type checking done by min() and max().
Posted by David Laight 2 years, 9 months ago
From: Andrew Morton
> Sent: 26 November 2022 00:02
> 
> On Fri, 25 Nov 2022 19:47:14 +0000 David Laight <David.Laight@ACULAB.COM> wrote:
> 
> > > Blindly relaxing the rules is not an option in my opinion.
> >
> > The point is I'm not really relaxing the rules.
> > If anything I'm actually tightening them by allowing min() to
> > be used in more cases - so letting the buggy min_t() casts be
> > removed at some point in the future.
> 
> That sounds very virtuous.

Indeed.

> It would be helpful to see a few "convert min_t to min" patches
> to see this proposal in action.

I did try building a kernel with '#define min_t min' and then
picking up the pieces.
Doable but there are still too many false positives.
That is when I found some of the 'broken' uses of min_t().

I'd rather get this patch accepted and then build on top of it.
Then it should be possible to work out which min_t() actually
need to stay - I suspect very few.

It might even be worth converting min_t(type,x,y) to
min_t_type(x,y) and then converting the min_t_u[0-9]*(x,y) to
#define min_unsigned(x,y) min((x) + 0u + 0ull, (y) + 0u + 0ull)
I think that efficiently converts both values to an unsigned
type without masking the value.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)