List Info

Thread: off_t madness




off_t madness
country flaguser name
Germany
2007-02-05 12:17:47
Hi,

the following shows what can wrong if off_t is used
incorrectly in comparisons.
I'm not sure whether it really yields a warning on 64-bit
archs (like AMD64),
it does for me if I fake a 64-bit size_t on a 32-bit arch.
Regardless of this
warning, things can still go wrong as shown below.

May I assume option 2 is the preferred way to write code
like this?

Technically, I think the compiler warning is wrong because
off_t is implicitely
promoted to a non-existing unsigned integer type (uint64_t
in our case) or if
size_t was wider, it would be converted to size_t. Thus,
this looks like a
false-positive to me. Careless use of explicit casts here
can cause real bugs
though (i.e., off_t should never be cast to size_t unless
you've already
checked that it fits).

#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>

#define MAX_INT_VAL_STEP(t) 
	((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t) -1 <
1)))
	 
#define MAX_INT_VAL(t) 
	((MAX_INT_VAL_STEP(t) - 1) + MAX_INT_VAL_STEP(t))

#define MIN_INT_VAL(t) 
	((t) -MAX_INT_VAL(t) - 1)

/* uint64_t for 64-bit arch, uint32_t for 32-bit arch */
typedef uint32_t my_size_t;

#define MY_SIZE_T_MAX MAX_INT_VAL(my_size_t)
#define OFF_T_MAX MAX_INT_VAL(off_t)

int main(void)
{
	my_size_t bufsize;
	off_t filesize;
	int ret;

	bufsize = -1;
	filesize = OFF_T_MAX;

	/*
	 * Question: 
	 *
	 * Does the file fit into the buffer? (example: mmap())
	 *
	 * ret = filesize >= 0 && filesize <=
bufsize;
	 *
	 * Problem:
	 *
	 * - comparison between signed and unsigned.
	 *
	 * - off_t is a signed integer type, an equivalent unsigned
integer
	 *   type (unsigned off_t?) does not exist.
	 *
	 * - size_t is architecture-dependent.
	 *
	 * - off_t is currently always int64_t on BSDs but not
everywhere
	 *   (int32_t on many legacy systems) and may change
(int128_t?).
	 *
	 * - OFF_T_MAX (maximum value of off_t) is unknown but can
be
	 *   inferred making a few assumptions that should be true
in
	 *   practice.
	 */

	/* Option 1 */

	ret = OFF_T_MAX > MY_SIZE_T_MAX
		? (filesize >= 0 && filesize <=
(off_t)bufsize)
		: (filesize >= 0 && (my_size_t)filesize <=
bufsize);

	/*
	 * WRONG if OFF_T_MAX is wrong.
	 */

	printf("1) filesize >= 0 && filesize <=
bufsize: %sn",
		ret ? "yes" : "no");

	/* Option 2 */

	ret = filesize >= 0
		&& (uintmax_t)(intmax_t)filesize <=
(uintmax_t)bufsize;

	/*
	 * WRONG if off_t is an extended integer type not covered
by
	 * intmax_t. This is probably rather unlikely, so it might
be
	 * the preferrable solution for ISO C99 code.
	 *
	 * example:
	 *
	 * typedef int64_t intmax_t;
	 * typedef __int128_t off_t;
	 */

	printf("2) filesize >= 0 && filesize <=
bufsize: %sn",
		ret ? "yes" : "no");

	/* Option 3 */

	ret = filesize > 0 && filesize <=
(off_t)bufsize;

	/*
	 * WRONG if OFF_T_MAX < SIZE_MAX
	 *
	 * example:
	 *
	 * typedef int64_t off_t;
	 * typedef uint64_t size_t;
	 *
	 * or:
	 * typedef int32_t off_t;
	 * typedef uint64_t size_t;
	 *
	 */

	printf("3) filesize >= 0 && filesize <=
bufsize: %sn",
		ret ? "yes" : "no");

	/* Option 4 */

	ret = filesize >= 0 && (size_t)filesize <=
bufsize;

	/*
	 * WRONG if SIZE_MAX < OFF_T_MAX
	 *
	 * example:
	 *
	 * typedef int64_t off_t;
	 * typedef uint64_t size_t;
	 *
	 * or:
	 * typedef int64_t off_t;
	 * typedef uint32_t size_t;
	 */

	printf("4) filesize >= 0 && filesize <=
bufsize: %sn",
		ret ? "yes" : "no");

	/* Option 5 */

	ret = filesize >= 0 && filesize <= bufsize;

	/*
	 * Just ignore the compiler warning.
	 */

	printf("5) filesize >= 0 && filesize <=
bufsize: %sn",
		ret ? "yes" : "no");

	return 0;
}

Re: off_t madness
country flaguser name
South Africa
2007-02-05 13:00:05
On Mon, 05 Feb 2007, Christian Biere wrote:
> #define MAX_INT_VAL_STEP(t) 
> 	((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t) -1
< 1)))
> 	 
> #define MAX_INT_VAL(t) 
> 	((MAX_INT_VAL_STEP(t) - 1) + MAX_INT_VAL_STEP(t))
> 
> #define MIN_INT_VAL(t) 
> 	((t) -MAX_INT_VAL(t) - 1)

Without comments, it's too difficult to figure out what you
are trying
to do here.

> 	 * Question: 
> 	 *
> 	 * Does the file fit into the buffer? (example:
mmap())
> 	 *
> 	 * ret = filesize >= 0 && filesize <=
bufsize;

> 	/* Option 1 */
> 
> 	ret = OFF_T_MAX > MY_SIZE_T_MAX
> 		? (filesize >= 0 && filesize <=
(off_t)bufsize)
> 		: (filesize >= 0 && (my_size_t)filesize
<= bufsize);
> 
> 	/*
> 	 * WRONG if OFF_T_MAX is wrong.
> 	 */

Ewww.  Even adding parentheses around the ?: term doesn't
make this
less ugly.

> 	/* Option 2 */
> 
> 	ret = filesize >= 0
> 		&& (uintmax_t)(intmax_t)filesize <=
(uintmax_t)bufsize;

This looks right (thought I would use parentheses around the
&& term).

> 
> 	/*
> 	 * WRONG if off_t is an extended integer type not
covered by
> 	 * intmax_t. This is probably rather unlikely, so it
might be
> 	 * the preferrable solution for ISO C99 code.
> 	 *
> 	 * example:
> 	 *
> 	 * typedef int64_t intmax_t;
> 	 * typedef __int128_t off_t;
> 	 */

That can't happen in C99.  intmax_t and unintmax_t are
defined as the
longest integral types (section 1.18.1.5 of draft N1124 of
the C99
standard).  There may be extended types longet than
"long long" or
longer than int64_t, but not longer then intmax_t.  (In C89,
"long" was
defined as the longest integral type, but common practice
soon broke
that.)

--apb (Alan Barrett)

Re: off_t madness
country flaguser name
Germany
2007-02-05 14:31:32
Alan Barrett wrote:
> On Mon, 05 Feb 2007, Christian Biere wrote:
> > #define MAX_INT_VAL_STEP(t) 
> > 	((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t)
-1 < 1)))
> > 	 
> > #define MAX_INT_VAL(t) 
> > 	((MAX_INT_VAL_STEP(t) - 1) +
MAX_INT_VAL_STEP(t))
> > 
> > #define MIN_INT_VAL(t) 
> > 	((t) -MAX_INT_VAL(t) - 1)
> 
> Without comments, it's too difficult to figure out what
you are trying
> to do here.

/*
 * These macros determine the maximum/minimum value of the
given integer type
 * "t". This works for signed as well as unsigned
types. This code does
 * carefully avoid integer overflows and undefined
behaviour.
 * However, it's assumed the type consists of exactly
sizeof(type) * CHAR_BIT
 * bits.
 */
 
> > 	/* Option 2 */
> > 
> > 	ret = filesize >= 0
> > 		&& (uintmax_t)(intmax_t)filesize <=
(uintmax_t)bufsize;
 
> This looks right (thought I would use parentheses
around the && term).

The (uintmax_t) before bufsize is actually unnecessary and
so it would
fit nicely into one line.

I rather avoid unnecessary parentheses (except in macros)
because I find
too many of them less readable and it can also suppress
valid compiler
warnings. Consider examples like this:

	ret = (filesize =! 0);
	ret = (filesize = 0);

I would also normally use a inline function to avoid
explicit casts in
random places:

static inline uintmax_t
off_to_uintmax(off_t offset)
{
	return (uintmax_t)(intmax_t)offset;
}

 	ret = filesize >= 0 && off_to_uintmax(filesize)
<= bufsize;

> intmax_t and unintmax_t are defined as the longest
integral types (section
> 1.18.1.5 of draft N1124 of the C99 standard).  There
may be extended types
> longet than "long long" or longer than
int64_t, but not longer then intmax_t.

Ok, thanks for your comments.

-- 
Christian

[1-3]

about | contact  Other archives ( Real Estate discussion Medical topics )