Date: Sun, 9 Jun 2002 12:33:47 +1000 (EST) From: Bruce Evans <bde@zeta.org.au> To: Giorgos Keramidas <keramida@FreeBSD.ORG> Cc: audit@FreeBSD.ORG Subject: Re: badsect warns Message-ID: <20020609123105.X21758-100000@gamplex.bde.org> In-Reply-To: <20020609011418.GA70449@hades.hell.gr>
next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, 9 Jun 2002, Giorgos Keramidas wrote:
> The following enables compiling badsect with WARNS=6.
> There seems to be something funny about fs->fs_size though.
> In <ufs/ffs/fs.h> it's not daddr_t but `long'.
It is actually int32_t, although it is logically a type a tiny bit
larger than ufs_daddr_t (ffs block numbers have type ufs_daddr_t and
the filesystem size is 1 larger than the largest ffs block number).
I think gcc is just warning about a sign mismatch now, but the cast
is almost necessary for correct code on machines with >= 32-bit
unsigned's. It "fixes" overflow of the sum.
fs->fs_size has type int64_t in ufs2, so the cast is just broken
except on machines with >= 64-bit unsigneds. ufs2 has similarly
broken casts internally, e.g., (u_int)bno in ffs_alloc.c.
> Is there some reason that this is cast to `unsigned'?
> %%%
> Index: badsect.c
> ===================================================================
> RCS file: /home/ncvs/src/sbin/badsect/badsect.c,v
> retrieving revision 1.13
> diff -u -r1.13 badsect.c
> --- badsect.c 16 May 2002 04:09:51 -0000 1.13
> +++ badsect.c 9 Jun 2002 01:00:54 -0000
> @@ -168,7 +168,7 @@
> daddr_t fsbn, bn;
>
> fsbn = dbtofsb(fs, blkno);
> - if ((unsigned)(fsbn+cnt) > fs->fs_size) {
> + if ((fsbn + cnt) > fs->fs_size) {
> printf("block %ld out of range of filesystem\n", (long)blkno);
> return (1);
> }
> %%%
The cast seems to be just the usual obsolete hack to check that a number
is >= 0 and <= a maximum value unsigned only one comparison:
int foo, max:
...
if ((u_int)foo > max)
err(...);
This is obsolete because non-primitive compilers will optimize the obvious
check:
if (foo < 0 || foo > max)
into the above if that is an optimization.
It fails to do this correctly in lots of ways:
1) The unsigned cast must be to the same or a wider type than the left
operand.
2) gcc tends to warn if the type of thr right operand is signed.
3) fsbn+cnt may have overflowed before we checked it. Casting the terms
of the sum would work better -- it prevents overflow provided the terms
are nonnegative. However, the terms are not known to be nonnegative here
because the caller has not checked its (user-supplied!) input -- `cnt' is
always 1, but fsbn is dbtofsb(fs, blkno) which is garbage if blkno is
garbage, and blkno may be garbage since it is just atol(*argv). This
leads to bugs in the caller:
(4) Disk block numbers are read in using atol(), but this can only work if
daddr_t has type long. This was broken on machines with 64-bit longs,
and ufs2 breaks it on machines with 32-bit longs.
(5) There is no overflow checking for atol() of course.
(6) Negative daddr_t's are errors in this context (probably in all contexts).
They are not checked for, but the checking in chkuse() depends on them
being weeded out earlier.
64-bit daddr_t's also break all the casts to long in error messages. 64-bit
daddr_t's can't be printed as longs if longs are 32 bits.
> If not, should we remove the cast and start building badsect with
> something more strict than WARNS=0?
Needs more extensive cleanups. Warnings about sign mismatches basically
just acientally tell you about overlow of the top bit here. Changing
daddr_t to 64 bits tends to cause overflow in 32 other bits.
Bruce
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020609123105.X21758-100000>
