Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 8 Dec 2013 13:53:30 +1100 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Justin Hibbits <jhibbits@freebsd.org>
Cc:        svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org
Subject:   Re: svn commit: r259080 - in head/sys: dev/iicbus geom/cache geom/journal
Message-ID:  <20131208123144.U883@besplex.bde.org>
In-Reply-To: <201312071955.rB7JtYNH001792@svn.freebsd.org>
References:  <201312071955.rB7JtYNH001792@svn.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Sat, 7 Dec 2013, Justin Hibbits wrote:

> Log:
>  Fix some integer signs.  These unsigned integers should all be signed.
>
>  Found by:	clang (powerpc64)

Unsigned integers are indeed used excessively.  They should only be used
if values larger than INT_MAX need to be represented, but values larger
than UINT_MAX need not be represented.  This is a very small target
(just 1 more bit).

> Modified: head/sys/dev/iicbus/max6690.c
> ==============================================================================
> --- head/sys/dev/iicbus/max6690.c	Sat Dec  7 19:39:38 2013	(r259079)
> +++ head/sys/dev/iicbus/max6690.c	Sat Dec  7 19:55:34 2013	(r259080)
> @@ -362,7 +362,7 @@ max6690_sensor_sysctl(SYSCTL_HANDLER_ARG
> 	struct max6690_softc *sc;
> 	struct max6690_sensor *sens;
> 	int error;
> -	unsigned int temp;
> +	int temp;
>
> 	dev = arg1;
> 	sc = device_get_softc(dev);
>

This fixes one style bug (non-use of the BSD spelling u_int) but adds
another (int declarations not grouped together).

> Modified: head/sys/geom/cache/g_cache.c
> ==============================================================================
> --- head/sys/geom/cache/g_cache.c	Sat Dec  7 19:39:38 2013	(r259079)
> +++ head/sys/geom/cache/g_cache.c	Sat Dec  7 19:55:34 2013	(r259080)
> @@ -67,7 +67,7 @@ static u_int g_cache_used_hi = 20;
> static int
> sysctl_handle_pct(SYSCTL_HANDLER_ARGS)
> {
> -	u_int val = *(u_int *)arg1;
> +	int val;
> 	int error;
>
> 	error = sysctl_handle_int(oidp, &val, 0, req);
>

This adds the larger non-style bug that val is used uninitialized.
Compilers should warn about this.  This results in stack garbage
being copied out instead of the "old" value.

sysctl_handle_u_int() doesn't exist, so sysctl_handle_int() os often
abused to handle u_int's and worse (sometimes "opaque" types, where
it usually works accidentally if the type is 32 bits, and sometimes
even works accidentally if the type is larger).  clang seems to be
complaining about this abuse.  Why does it only complain here?  Ah,
maybe it is not smart enough to complain about this, but is just
complaining about the (val < 0) range check.

This adds 2 (lexical) style bugs:
- the int declarations are not grouped together

This misses fixing nearby bogus unsigned variables.  The cast for these
is more bogus than before.  Indeed, the whole function is horrible, and
this change is sort of backwards since it gives a sign mismatch with
the underlying variables:

% static u_int g_cache_used_lo = 5;
% static u_int g_cache_used_hi = 20;

Example of excessive use of unsigned types.  These variables only need
to handle percentages between 0 and 100.

% static int

Style bug (missing blank line);

% sysctl_handle_pct(SYSCTL_HANDLER_ARGS)
% {
% 	u_int val = *(u_int *)arg1;

This is the old version.  It has another style bug (initialization in
declaration).  The initialization wouldn't have been lost so easily
if it had been in the correct place.

But the type was correct, since it is bug-for-bug constent with the
underlying variables.

% 	int error;
% 
% 	error = sysctl_handle_int(oidp, &val, 0, req);

sysctl_handle_int() was abused to handle the u_int (arg1 points to
one of the above 2 variables).

% 	if (error || !req->newptr)
% 		return (error);

2 style bugs (2 boolean comparisions of non-booleans).  Strict KNF doesn't
even use the ! operator to test booleansl; it uses an explicit " == 0"
test for this, though it sometimes doesn't write out "!= 0" for the reverse
test.

% 	if (val < 0 || val > 100)
% 		return (EINVAL);

The first test in this is redundant,  This is apparently what clang was
complaining about.  The warning for that can be fixed by removing the
test.  However, I don't like this fix.  It depends on the type being
unsigned.  The code becomes more fragile, and would break if the type
were changed to the correct one (signed).

% 	if ((arg1 == &g_cache_used_lo && val > g_cache_used_hi) ||
% 	    (arg1 == &g_cache_used_hi && g_cache_used_lo > val))
% 		return (EINVAL);
% 	*(u_int *)arg1 = val;

There are only 2 variables handled by this function, so we use arg1 
to set the correct one.  However, the previous statement has decided
which of the variables is being set, and we could use that to set it.
Or better, convert arg1 from void * to u_int * up front, and use that
throughout instead of several casts of arg1.

% 	return (0);
% }
% SYSCTL_PROC(_kern_geom_cache, OID_AUTO, used_lo, CTLTYPE_UINT|CTLFLAG_RW,

Style bugs:
- missing blank line.  This is intentional for grouping the function with
   its variables, as above, but still ugly.
- missing spaces around binary operator '|'.  This bug is too common for
   this operator, and for CTLFLAG*.  But the spaces are even more needed
   for '|' than for '+' or '*', since '|' looks more like an alphanumeric
   character.

% 	&g_cache_used_lo, 0, sysctl_handle_pct, "IU", "");
% SYSCTL_PROC(_kern_geom_cache, OID_AUTO, used_hi, CTLTYPE_UINT|CTLFLAG_RW,
% 	&g_cache_used_hi, 0, sysctl_handle_pct, "IU", "");

Style bugs:
- non-KNF continuation indent.  This bug is too common for SYSCTL*(),
   although someone once did a fairly global sweep to fix it for them.
- no description.

This sysctl is over-engineered.  A simple SYSCTL_UINT() is enough.
There are many more-critical sysctls that don't have any range checking.
The fix is not to bloat the kernel by adding range checking to them
all.  With several thousand sysctls, the range checking code could
easily be over-engineered enough to double the size of an already
massively bloated kernel.  OTOH, there may still be runtime errors
from sloppy caclulations using the percentage variables.  The
calculations are of the form (x * pct / 100) and not the more robust
but fuzzier (x / 100 * pct).  pct will normally be promoted to the
type of x in these expressions (often to 64 bits, and then overflow
is far off).  Overflow is only affected by pct being unsigned if x is
int or shorter.

> Modified: head/sys/geom/journal/g_journal.c
> ==============================================================================
> --- head/sys/geom/journal/g_journal.c	Sat Dec  7 19:39:38 2013	(r259079)
> +++ head/sys/geom/journal/g_journal.c	Sat Dec  7 19:55:34 2013	(r259080)
> @@ -168,7 +168,7 @@ SYSCTL_UINT(_kern_geom_journal_cache, OI
> static int
> g_journal_cache_switch_sysctl(SYSCTL_HANDLER_ARGS)
> {
> -	u_int cswitch;
> +	int cswitch;
> 	int error;
>
> 	cswitch = g_journal_cache_switch;
>

2 style bugs (int variables not grouped; int variables not sorted).

This sysctl is missing most of the style bugs discussed above:
- the transfer variable cswitch is not initialized in its declaration.
   Its initialization didn't get deleted.
- the comparisons of the non-booleans are non-boolean.
- the percentage variable is used in the robust way (x / 100 * pct).
- the SYSCTL()s use KNF continuation indentation
- there are spaces around the binary operator '|'
- the SYSCTL()s have descriptions.

This leaves mainly type errors (excessive use of unsigned variables).
There is a different inconsistency: the u_int g_journal_cache_switch
has formatting data "I" instead of "IU".  Both will work due to the
range checking and assumptions that the machine is not exotic, but there
is no reason to not to use a consistent format.

Another common style bug/API design error visible here is using
sysctl_handle_int() on generic variables.  You have to know that the
variable type is int to use it or u_int, int32_t or uint32_t to abuse
it.  The width of the variable is in the sysctl data and is usually
correct.  sysctl_handle_opaque() is usable on generic variables but
doesn't work so well (it prevents type checking and has more locking
overheads).  When sysctl_handle_int() is used, the width in the sysctl
data should be used to detect size mismatches.  It is used, but there
are bugs in the error handling for this (something like ignoring size
mismatches for short i/o's in only 1 direction).

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20131208123144.U883>