Skip site navigation (1)Skip section navigation (2)
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>