Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 1 Jun 2015 20:05:01 +0200
From:      Baptiste Daroussin <bapt@freebsd.org>
To:        Bruce Evans <brde@optusnet.com.au>
Cc:        svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org
Subject:   Re: svn commit: r283842 - head/usr.sbin/pw
Message-ID:  <20150601180501.GA7523@ivaldir.etoilebsd.net>
In-Reply-To: <20150601143951.J863@besplex.bde.org>
References:  <201505312207.t4VM73Vh015383@svn.freebsd.org> <20150601143951.J863@besplex.bde.org>

next in thread | previous in thread | raw e-mail | index | archive | help

--tKW2IUtsqtDRztdT
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Mon, Jun 01, 2015 at 03:36:58PM +1000, Bruce Evans wrote:
> On Sun, 31 May 2015, Baptiste Daroussin wrote:
>=20
> > Log:
> >  Remove useless cast in printf and printf-like functions:
> >  use %u for uid_t and gid_t
>=20
> The cast was not useless.  It was to avoid the assumption that the default
> promotion of uid_t and gid_t is anything in particular.  Now it is assumed
> that the default promotion is unsigned (int works too, but this is subtle=
r).
>=20
> uids and gids are only guaranteed to have non-negative values.  In
> POSIX before about 2001, uid_t and gid_t can be any type that can
> represent all the values taken on, so can be floating point.  Floating
> point was rarely used for POSIX types, and most programs make too many
> assumptions about types, so POSIX now requires uid_t and gid_t to be
> integer types.  Then can still be signed integer types IIRC.  Nornally
> it is a bug to print signed integer types with unsigned integer formats,
> but since uids and gids are guaranteed to be non-negative both formats
> work.  (pids require different handling since they are overloaded to
> hold process group ids as negative values, so pid_t is signed and %u
> format is very broken for printing general pids.)

Well uid_t is defined as an unsigned int (__uint32_t actually) if it is ever
going to be changed to something else they at least it will now raise a war=
ning.

I consider using %u here is less buggy than the previous casts.
>=20
> The program assumed that uids and gids are not too large to be represented
> by unsigned long.  This was the only way to print them in C90 and before.
> C99 broke this by breaking the promise that unsigned long is the largest
> unsigned integer type.  This broke all code that does careful casts to
> unsigned long.  However, unsigned long is usually large enough in practic=
e.
> Careful code now has to cast to uintmax_t, but that is usually excessive
> (but doesn't actually work for __uint128_t).  Even plain unsigned usually
> works on vaxes.
>=20
> > Modified:
> >  head/usr.sbin/pw/pw_conf.c
> >  head/usr.sbin/pw/pw_group.c
> >  head/usr.sbin/pw/pw_user.c
> >
> > Modified: head/usr.sbin/pw/pw_conf.c
> > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D
> > --- head/usr.sbin/pw/pw_conf.c	Sun May 31 21:44:09 2015	(r283841)
> > +++ head/usr.sbin/pw/pw_conf.c	Sun May 31 22:07:03 2015	(r283842)
> > @@ -453,19 +453,19 @@ write_userconfig(char const * file)
> > 			    config.default_class : "");
> > 			break;
> > 		case _UC_MINUID:
> > -			sbuf_printf(buf, "%lu", (unsigned long) config.min_uid);
> > +			sbuf_printf(buf, "%u", config.min_uid);
>=20
> This works accidentally even on some non-vaxes.  All supported arches in
> FreeBSD have 32-bit unsigned's, and POSIX was broken in about 2007 to
> disallow 16-bit unsigned's.  uid_t is uint32_t in FreeBSD.  However, it
> is planned to change ino_t and some other types to 64 bits.  I forget if
> the plans include uid_t.  Even 64-bit ino_t is too large for me.
>=20
> The version with the cast to unsigned long is not really better.  Suppose
> uid_t is expanded to 16 bits.  Then without the cast, the size mismatch
> is detected.  With it, everything still works if unsigned long is 64 bits,
> but if unsigned long is 32 bits then the cast just breaks detection of
> the mismatch and gives wrong results for values that actually need 64 bit=
s.
>=20
> > Modified: head/usr.sbin/pw/pw_group.c
> > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D
> > --- head/usr.sbin/pw/pw_group.c	Sun May 31 21:44:09 2015	(r283841)
> > +++ head/usr.sbin/pw/pw_group.c	Sun May 31 22:07:03 2015	(r283842)
> > @@ -83,7 +83,7 @@ pw_group(struct userconf * cnf, int mode
> > 		gid_t next =3D gr_gidpolicy(cnf, args);
> > 		if (getarg(args, 'q'))
> > 			return next;
> > -		printf("%ld\n", (long)next);
> > +		printf("%u\n", next);
> > 		return EXIT_SUCCESS;
> > 	}
> >
>=20
> This also has sign changes.  gids are supposed to be non-negative, so
> using signed long was probably a bug.
>=20
> However, the program is elsewhere clueless about types, and I think you
> did't change the parts where it uses atol() to misparse input.  atol()
> can't even handle 32-bit uids on 32-bit arches.  It truncates large
> positive ones to LONG_MAX.  It returns negative values (truncated to
> LONG_MIN if necessary).  The misparsing then doesn't detect the error
> of negative ids, but blindly casts to uid_t or gid_t, of course using
> lexical style bugs in the casts.  The magic ids -1 and -2 are sometimes
> needed (perhaps not as input in pw, but -1 is used as an error value
> and this value can be input).  These values don't really exist as ids.
> They must be cast before use.  The actual values just cannot be entered,
> since atol() misparses them (it truncates large values, and also doesn't
> support hex, so (uid_t)-2 must be written as a long decimal number to
> input it for misparsing.

I do plan to work on those atol as well, but I try to be very careful with
pw(8). So I make small modifications one by one.
>=20
> > @@ -137,7 +137,7 @@ pw_group(struct userconf * cnf, int mode
> > 			else if (rc !=3D 0) {
> > 				err(EX_IOERR, "group update");
> > 			}
> > -			pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) g=
id);
> > +			pw_log(cnf, mode, W_GROUP, "%s(%u) removed", a_name->val, gid);
> > 			return EXIT_SUCCESS;
> > 		} else if (mode =3D=3D M_PRINT)
> > 			return print_group(grp, getarg(args, 'P') !=3D NULL);
>=20
> If the error gid (gid_t)-1 is ever printed, now %u format prints it as a
> cryptic decimal number where %ld format printed it as -1.

well actually this code was actually used, and I checked the output before =
and
after the change and it printed the same output 4294967295.

Best regards,
Bapt

--tKW2IUtsqtDRztdT
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iEYEARECAAYFAlVsns0ACgkQ8kTtMUmk6ExM/wCgnqGE+gb3+K6o8x8qWsIpuibc
JmIAnR31xOghBTsEQ83xNzIECu2OI7rA
=ZPS8
-----END PGP SIGNATURE-----

--tKW2IUtsqtDRztdT--



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